├── .gitignore ├── README.md └── code ├── 354984si.ngl.gz ├── Makefile ├── Makefile.template ├── a.erl ├── abc.erl ├── allocator.erl ├── area_server.erl ├── area_server0.erl ├── area_server1.erl ├── area_server2.erl ├── area_server_final.erl ├── attrs.erl ├── b.erl ├── bad.erl ├── benchmark_assoc.erl ├── benchmark_mk_assoc.erl ├── broadcast.erl ├── chat_cluster.erl ├── chat_file_transfer.erl ├── chat_multi.erl ├── chat_secure.erl ├── chat_socket.erl ├── checker.erl ├── clock.erl ├── convert1.erl ├── convert2.erl ├── convert3.erl ├── convert4.erl ├── convert5.erl ├── cookbook_examples.erl ├── counter1.erl ├── counter2.erl ├── counter3.erl ├── counter4.erl ├── ctemplate.erl ├── data1.dat ├── dist_demo.erl ├── edemo1.erl ├── edemo2.erl ├── elog1.config ├── elog2.config ├── elog3.config ├── elog4.config ├── elog5.config ├── emacs.setup ├── error1.erl ├── ets_test.erl ├── event_handler.erl ├── exprs.dat ├── extract.erl ├── fac.erl ├── fac1.erl ├── factorial ├── gathered.html ├── gen_server_template.full ├── gen_server_template.mini ├── geometry.erl ├── hello ├── hello.bat ├── hello.erl ├── hello.sh ├── id3_tag_lengths.erl ├── id3_v1.erl ├── indexer-1.1 ├── Makefile ├── Readme ├── indexer.erl ├── indexer_checkpoint.erl ├── indexer_dir_crawler.erl ├── indexer_filenames_dets.erl ├── indexer_misc.erl ├── indexer_porter.erl ├── indexer_server.erl ├── indexer_trigrams.erl └── indexer_words.erl ├── lib_auth_cs.erl ├── lib_filenames_dets.erl ├── lib_files_find.erl ├── lib_find.erl ├── lib_io_widget.erl ├── lib_lin.erl ├── lib_md5.erl ├── lib_misc.erl ├── lib_primes.erl ├── lib_rsa.erl ├── lib_tcp_server.erl ├── lib_trigrams.erl ├── lib_trigrams_complete.erl ├── lists1.erl ├── m1.erl ├── math1.erl ├── math2.erl ├── math3.erl ├── misc.erl ├── monitor1.erl ├── monitor2.erl ├── monitor3.erl ├── motor_controller.erl ├── mp3_manager.erl ├── mp3_sync.erl ├── my_alarm_handler.erl ├── my_bank.erl ├── my_fac_server.erl ├── mylists.erl ├── name_server.erl ├── name_server1.erl ├── new_name_server.erl ├── phofs.erl ├── ports ├── Makefile ├── erl_comm.c ├── erl_driver.h ├── example1.c ├── example1.erl ├── example1_driver.c ├── example1_lid.c ├── example1_lid.erl ├── log └── port_driver.c ├── prime_server.erl ├── processes.erl ├── ptests.erl ├── readme.trigrams ├── records.hrl ├── registrar.erl ├── runtests ├── scavenge_urls.erl ├── sellaprime.app ├── sellaprime_app.erl ├── sellaprime_supervisor.erl ├── server1.erl ├── server2.erl ├── server3.erl ├── server4.erl ├── server5.erl ├── shop.erl ├── shop1.erl ├── shop2.erl ├── shop3.erl ├── shout.erl ├── simple.app ├── socket_dist ├── Makefile ├── any_apply.erl ├── chat.conf ├── chat_client.erl ├── chat_group.erl ├── chat_server.erl ├── conf ├── config1 ├── io_widget.erl ├── kvs.erl ├── lib_chan.erl ├── lib_chan_auth.erl ├── lib_chan_cs.erl ├── lib_chan_mm.erl ├── lib_chan_test.erl ├── mod_chat_controller.erl ├── mod_echo.erl ├── mod_math.erl ├── mod_name_server.erl ├── mod_srpc.erl ├── readme ├── test1.erl └── test_name_server.erl ├── socket_examples.erl ├── status.erl ├── stdmacros.hrl ├── stimer.erl ├── terms1.dat ├── terms1.dat.tmp ├── test1.erl ├── test_mapreduce.erl ├── test_mnesia.erl ├── tracer_test.erl ├── try_test.erl ├── udp_test.erl ├── upcase.erl ├── update_binary_file.erl ├── update_file.erl ├── user_default.erl ├── validate.dat ├── vfs.erl ├── vshlr1.erl ├── vshlr2.erl └── wordcount.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | deps 3 | priv 4 | *.o 5 | *.beam 6 | *.plt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | programming-erlang-code 2 | ======================= 3 | 4 | erlang codes in "Programming Erlang" http://pragprog.com/book/jaerlang/programming-erlang -------------------------------------------------------------------------------- /code/354984si.ngl.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everpeace/programming-erlang-code/8ef31aa13d15b41754dda225c50284915c29cb48/code/354984si.ngl.gz -------------------------------------------------------------------------------- /code/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .erl .beam .yrl 2 | 3 | .erl.beam: 4 | erlc -W $< 5 | 6 | MODS = a b abc area_server_final allocator\ 7 | benchmark_assoc benchmark_mk_assoc attrs chat_multi chat_socket\ 8 | chat_cluster chat_file_transfer \ 9 | chat_secure checker clock ctemplate\ 10 | dist_demo edemo1 edemo2 ets_test event_handler extract fac fac1\ 11 | geometry id3_v1 hello m1 upcase\ 12 | id3_tag_lengths \ 13 | lib_find lib_io_widget lib_files_find lib_lin\ 14 | lib_filenames_dets lib_primes \ 15 | lib_auth_cs lib_md5 lib_misc\ 16 | lib_rsa lib_tcp_server math1 math2 math3 my_fac_server name_server\ 17 | name_server1\ 18 | area_server area_server1 area_server0 motor_controller\ 19 | mp3_manager my_bank area_server1 area_server2\ 20 | monitor1 monitor2 monitor3 my_alarm_handler\ 21 | new_name_server broadcast convert1 convert2 convert3\ 22 | convert4 convert5 mylists lists1\ 23 | counter1 counter2 counter3 counter4 error1 cookbook_examples\ 24 | misc mp3_sync phofs prime_server processes ptests registrar\ 25 | sellaprime_app sellaprime_supervisor shop \ 26 | scavenge_urls shop1 shop2 shop3 socket_examples shout stimer\ 27 | server1 server2 server3 server4 server5\ 28 | status test_mapreduce test_mnesia tracer_test try_test\ 29 | udp_test update_binary_file update_file user_default vfs wordcount 30 | 31 | ERL = erl -boot start_clean 32 | 33 | compile: ${MODS:%=%.beam} subdirs trigramsOS.tab 34 | @echo "make clean - clean up" 35 | 36 | shoutcast: compile 37 | erl -s shout start 38 | 39 | subdirs: 40 | cd socket_dist; make compile 41 | cd escript-4.1; make 42 | 43 | counter1.beam: counter1.erl 44 | erlc -W0 counter1.erl 45 | 46 | m1.beam: m1.erl 47 | erlc -Ddebug m1.erl 48 | 49 | all: compile 50 | 51 | 52 | trigramsOS.tab: 354984si.ngl.gz lib_trigrams.beam 53 | @erl -noshell -boot start_clean -s lib_trigrams make_tables\ 54 | -s init stop 55 | 56 | timer_tests: 57 | @erl -noshell -boot start_clean -s lib_trigrams timer_tests\ 58 | -s init stop 59 | clean: 60 | rm -rf *.beam lists.ebeam erl_crash.dump 61 | rm -rf trigramsOS.tab trigramsS.tab trigrams.dict 62 | cd ets_trigrams; make_clean 63 | cd socket_dist; make_clean 64 | 65 | -------------------------------------------------------------------------------- /code/Makefile.template: -------------------------------------------------------------------------------- 1 | # leave these lines alone 2 | .SUFFIXES: .erl .beam .yrl 3 | 4 | .erl.beam: 5 | erlc -W $< 6 | 7 | .yrl.erl: 8 | erlc -W $< 9 | 10 | ERL = erl -boot start_clean 11 | 12 | # Here's a list of the erlang modules you want compiling 13 | # If the modules don't fit onto one line add a \ character 14 | # to the end of the line and continue on the next line 15 | 16 | # Edit the lines below 17 | MODS = module1 module2 \ 18 | module3 ... special1 ...\ 19 | ... 20 | moduleN 21 | 22 | # The first target in any makefile is the default target. 23 | # If you just type "make" then "make all" is assumed (because 24 | # "all" is the first target in this makefile) 25 | 26 | all: compile 27 | 28 | compile: ${MODS:%=%.beam} subdirs 29 | 30 | ## special compilation requirements are added here 31 | 32 | special1.beam: special1.erl 33 | ${ERL} -Dflag1 -W0 special1.erl 34 | 35 | ## run an application from the makefile 36 | 37 | application1: compile 38 | ${ERL} -pa Dir1 -s application1 start Arg1 Arg2 39 | 40 | # the subdirs target compiles any code in 41 | # sub-directories 42 | 43 | subdirs: 44 | cd dir1; make 45 | cd dir2; make 46 | ... 47 | 48 | # remove all the code 49 | 50 | clean: 51 | rm -rf *.beam erl_crash.dump 52 | cd dir1; make clean 53 | cd dir2; make clean 54 | -------------------------------------------------------------------------------- /code/a.erl: -------------------------------------------------------------------------------- 1 | -module(a). 2 | -compile(export_all). 3 | 4 | start(Tag) -> 5 | spawn(fun() -> loop(Tag) end). 6 | 7 | loop(Tag) -> 8 | sleep(), 9 | Val = b:x(), 10 | io:format("Vsn1 (~p) b:x() = ~p~n",[Tag, Val]), 11 | loop(Tag). 12 | 13 | sleep() -> 14 | receive 15 | after 3000 -> true 16 | end. 17 | -------------------------------------------------------------------------------- /code/abc.erl: -------------------------------------------------------------------------------- 1 | -module(abc). 2 | -export([a/2, b/1]). 3 | 4 | a(X, Y) -> c(X) + a(Y). 5 | a(X) -> 2 * X. 6 | b(X) -> X * X. 7 | c(X) -> 3 * X. 8 | -------------------------------------------------------------------------------- /code/allocator.erl: -------------------------------------------------------------------------------- 1 | -module(allocator). 2 | 3 | -export([start/0, alloc/0, free/1, status/0]). 4 | 5 | %% alloc() -> {yes, Resource} | no 6 | %% free(Resource) 7 | 8 | start() -> 9 | register(allocator1, spawn(fun() -> run() end)). 10 | 11 | alloc() -> rpc(alloc). 12 | free(X) -> rpc({free, X}). 13 | status() -> rpc(status). 14 | 15 | rpc(Q) -> 16 | allocator1 ! {self(), Q}, 17 | receive 18 | {allocator1, Reply} -> 19 | Reply 20 | end. 21 | 22 | init() -> 23 | [1,2,3,4,5,6,7,8,9,10]. 24 | 25 | run() -> 26 | process_flag(trap_exit, true), 27 | loop(init(), []). 28 | 29 | loop(Free, Used) -> 30 | receive 31 | {From, alloc} -> 32 | case Free of 33 | [] -> 34 | From ! {allocator1, no}, 35 | loop(Free, Used); 36 | [H|T] -> 37 | From ! {allocator1, {yes, H}}, 38 | link(From), 39 | loop(T, [{H,From}|Used]) 40 | end; 41 | {From, {dealloc, X}} -> 42 | Used1 = delete({X, From}, Used), 43 | case count_allocated(From, Used1) of 44 | 0 -> unlink(From); 45 | _ -> void 46 | end, 47 | From ! {allocator1, ack}, 48 | loop([X|Free], Used1); 49 | {From, status} -> 50 | From ! {allocator1, {free, Free, used, Used}}, 51 | loop(Free, Used); 52 | {'EXIT', Pid, _Why} -> 53 | {Used1, Free1} = dealloc(Pid, Used, [], Free), 54 | loop(Free1, Used1) 55 | end. 56 | 57 | delete(H, [H|T]) -> T; 58 | delete(H, [H1|T]) -> [H1|delete(H, T)]. 59 | 60 | dealloc(Pid, [{H,Pid}|T], Used, Free) -> dealloc(Pid, T, Used, [H|Free]); 61 | dealloc(Pid, [X|T], Used, Free) -> dealloc(Pid, T, [X|Used], Free); 62 | dealloc(_, [], Used, Free) -> {Used, Free}. 63 | 64 | count_allocated(Pid, [{_,Pid}|T]) -> 1 + count_allocated(Pid, T); 65 | count_allocated(Pid, [_|T]) -> count_allocated(Pid, T); 66 | count_allocated(_, []) -> 0. 67 | -------------------------------------------------------------------------------- /code/area_server.erl: -------------------------------------------------------------------------------- 1 | -module(area_server). 2 | -behaviour(gen_server). 3 | 4 | -export([area/1, start_link/0]). 5 | 6 | %% gen_server callbacks 7 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 8 | terminate/2, code_change/3]). 9 | 10 | start_link() -> 11 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 12 | 13 | area(Thing) -> 14 | gen_server:call(?MODULE, {area, Thing}). 15 | 16 | init([]) -> 17 | %% Note we must set trap_exit = true if we 18 | %% want terminate/2 to be called when the application 19 | %% is stopped 20 | process_flag(trap_exit, true), 21 | io:format("~p starting~n",[?MODULE]), 22 | {ok, 0}. 23 | 24 | handle_call({area, Thing}, _From, N) -> {reply, compute_area(Thing), N+1}. 25 | 26 | handle_cast(_Msg, N) -> {noreply, N}. 27 | 28 | handle_info(_Info, N) -> {noreply, N}. 29 | 30 | terminate(_Reason, _N) -> 31 | io:format("~p stopping~n",[?MODULE]), 32 | ok. 33 | 34 | code_change(_OldVsn, N, _Extra) -> {ok, N}. 35 | 36 | compute_area({square, X}) -> X*X; 37 | compute_area({rectongle, X, Y}) -> X*Y. 38 | -------------------------------------------------------------------------------- /code/area_server0.erl: -------------------------------------------------------------------------------- 1 | -module(area_server0). 2 | -export([loop/0]). 3 | 4 | loop() -> 5 | receive 6 | {rectangle, Width, Ht} -> 7 | io:format("Area of rectangle is ~p~n",[Width * Ht]), 8 | loop(); 9 | {circle, R} -> 10 | io:format("Area of circle is ~p~n", [3.14159 * R * R]), 11 | loop(); 12 | Other -> 13 | io:format("I don't know what the area of a ~p is ~n",[Other]), 14 | loop() 15 | end. 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /code/area_server1.erl: -------------------------------------------------------------------------------- 1 | -module(area_server1). 2 | -export([loop/0, rpc/2]). 3 | 4 | 5 | rpc(Pid, Request) -> 6 | Pid ! {self(), Request}, 7 | receive 8 | Response -> 9 | Response 10 | end. 11 | 12 | 13 | loop() -> 14 | receive 15 | {From, {rectangle, Width, Ht}} -> 16 | From ! Width * Ht, 17 | loop(); 18 | {From, {circle, R}} -> 19 | From ! 3.14159 * R * R, 20 | loop(); 21 | {From, Other} -> 22 | From ! {error,Other}, 23 | loop() 24 | end. 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /code/area_server2.erl: -------------------------------------------------------------------------------- 1 | -module(area_server2). 2 | -export([loop/0, rpc/2]). 3 | 4 | 5 | rpc(Pid, Request) -> 6 | Pid ! {self(), Request}, 7 | receive 8 | {Pid, Response} -> 9 | Response 10 | end. 11 | 12 | 13 | loop() -> 14 | receive 15 | {From, {rectangle, Width, Ht}} -> 16 | From ! {self(), Width * Ht}, 17 | loop(); 18 | {From, {circle, R}} -> 19 | From ! {self(), 3.14159 * R * R}, 20 | loop(); 21 | {From, Other} -> 22 | From ! {self(), {error,Other}}, 23 | loop() 24 | end. 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /code/area_server_final.erl: -------------------------------------------------------------------------------- 1 | -module(area_server_final). 2 | -export([start/0, area/2]). 3 | 4 | start() -> spawn(fun loop/0). 5 | 6 | area(Pid, What) -> 7 | rpc(Pid, What). 8 | 9 | rpc(Pid, Request) -> 10 | Pid ! {self(), Request}, 11 | receive 12 | {Pid, Response} -> 13 | Response 14 | end. 15 | 16 | 17 | loop() -> 18 | receive 19 | {From, {rectangle, Width, Ht}} -> 20 | From ! {self(), Width * Ht}, 21 | loop(); 22 | {From, {circle, R}} -> 23 | From ! {self(), 3.14159 * R * R}, 24 | loop(); 25 | {From, Other} -> 26 | From ! {self(), {error,Other}}, 27 | loop() 28 | end. 29 | 30 | -------------------------------------------------------------------------------- /code/attrs.erl: -------------------------------------------------------------------------------- 1 | -module(attrs). 2 | -vsn(1234). 3 | -author({joe,armstrong}). 4 | -purpose("example of attributes"). 5 | -export([fac/1]). 6 | 7 | fac(1) -> 1; 8 | fac(N) -> N * fac(N-1). 9 | -------------------------------------------------------------------------------- /code/b.erl: -------------------------------------------------------------------------------- 1 | -module(b). 2 | -export([x/0]). 3 | 4 | x() -> 1. 5 | -------------------------------------------------------------------------------- /code/bad.erl: -------------------------------------------------------------------------------- 1 | -module(bad). 2 | 3 | %% There are lots's of delibeate errors in this file 4 | %% so it's not in the makefile 5 | 6 | 7 | foo(1,2) -> 8 | a; 9 | foo(2,3,a) -> 10 | b. 11 | 12 | 13 | 14 | foo(A, B) -> 15 | bar(A, dothis(X), B), 16 | baz(Y, X). 17 | 18 | 19 | 20 | foo() -> 21 | case bar() of 22 | 1 -> 23 | X = 1, 24 | Y = 2; 25 | 2 -> 26 | X = 3 27 | end, 28 | b(X, Y). 29 | 30 | 31 | 32 | foo() -> 33 | case bar() of 34 | 1 -> 35 | X = 1, 36 | Y = 2; 37 | 2 -> 38 | X = 3 39 | end, 40 | b(X). 41 | 42 | 43 | 44 | foo(X, L) -> 45 | lists:map(fun(X) -> 2*X end, L). 46 | 47 | 48 | 49 | foo(X, L) -> 50 | lists:map(fun(Z) -> 2*Z end, L). 51 | 52 | 53 | 54 | foo(X) -> 55 | io:format("hello ~p~n, [X]). 56 | 57 | -------------------------------------------------------------------------------- /code/benchmark_mk_assoc.erl: -------------------------------------------------------------------------------- 1 | -module(benchmark_mk_assoc). 2 | 3 | -compile(export_all). 4 | 5 | test(N) -> 6 | [{list,dict,ets}, 7 | {method1(N), method2(N),method3(N)}]. 8 | 9 | method1(N) when N > 20000 -> 10 | false; 11 | method1(N) -> 12 | {T, _} = timer:tc(?MODULE, make_assoc_list1, [N]), 13 | trunc(T/N). 14 | 15 | method2(N) when N > 300000 -> 16 | false; 17 | method2(N) -> 18 | random_seed(), 19 | D = dict:new(), 20 | {T, _} = timer:tc(?MODULE, make_random_list2, [N, D]), 21 | trunc(T/N). 22 | 23 | method3(N) -> 24 | random_seed(), 25 | E = ets:new(sometable,[]), 26 | {T, _} = timer:tc(?MODULE, make_random_list3, [N, E]), 27 | ets:delete(E), 28 | trunc(T/N). 29 | 30 | make_random_list3(0, E) -> E; 31 | make_random_list3(N, E) -> 32 | %% Key = list_to_binary(random_string(4,6,$a, 26)), 33 | Key = (random_string(4,6,$a, 26)), 34 | case ets:lookup(E, Key) of 35 | [] -> 36 | Val = list_to_integer(random_string(2,6,$0,10)), 37 | ets:insert(E, {Key, Val}), 38 | make_random_list3(N-1 , E); 39 | _ -> 40 | make_random_list3(N, E) 41 | end. 42 | 43 | make_random_list2(0, D) -> dict:to_list(D); 44 | make_random_list2(N, D) -> 45 | %% Key = list_to_binary(random_string(4,6,$a, 26)), 46 | Key = (random_string(4,6,$a, 26)), 47 | case dict:find(Key, D) of 48 | error -> 49 | Val = list_to_integer(random_string(2,6,$0,10)), 50 | D1 = dict:store(Key, Val, D), 51 | make_random_list2(N-1 , D1); 52 | {ok, _} -> 53 | make_random_list2(N, D) 54 | end. 55 | 56 | 57 | make_assoc_list1(N) -> 58 | random_seed(), 59 | make_random_list1(N, []). 60 | 61 | %% goes like N^2 62 | 63 | make_random_list1(0, L) -> L; 64 | make_random_list1(N, L) -> 65 | %% Key = list_to_binary(random_string(4,6,$a, 26)), 66 | Key = (random_string(4,6,$a, 26)), 67 | case is_defined(Key, L) of 68 | true -> 69 | make_random_list1(N , L); 70 | false -> 71 | Val = list_to_integer(random_string(2,6,$0,10)), 72 | make_random_list1(N-1, [{Key,Val}|L]) 73 | end. 74 | 75 | is_defined(Key, [{Key,_}|_]) -> true; 76 | is_defined(Key, [_|T]) -> is_defined(Key, T); 77 | is_defined(_, []) -> false. 78 | 79 | random_string(Min, Extra, Start, K) -> 80 | Length = Min + random:uniform(Extra), 81 | random_str(Length, Start, K, []). 82 | 83 | random_str(0, _, _, D) -> 84 | D; 85 | random_str(N, Start, K, D) -> 86 | random_str(N-1, Start, K, [random:uniform(K)+ Start - 1|D]). 87 | 88 | random_seed() -> 89 | {_,_,X} = erlang:now(), 90 | {H,M,S} = time(), 91 | H1 = H * X rem 32767, 92 | M1 = M * X rem 32767, 93 | S1 = S * X rem 32767, 94 | put(random_seed, {H1,M1,S1}). 95 | 96 | %% 1> benchmark_mk_assoc:test(20000). 97 | %% [{list,dict,ets},{1149,82,42}] 98 | %% 2> c(benchmark_mk_assoc). 99 | %% {ok,benchmark_mk_assoc} 100 | %% 3> benchmark_mk_assoc:test(30000). 101 | %% [{list,dict,ets},{false,80,46}] 102 | %% 4> benchmark_mk_assoc:test(100000). 103 | %% [{list,dict,ets},{false,118,42}] 104 | %% 5> benchmark_mk_assoc:test(200000). 105 | %% [{list,dict,ets},{false,235,59}] 106 | %% 6> c(benchmark_mk_assoc). 107 | %% ./benchmark_mk_assoc.erl:15: syntax error before: '/' 108 | %% ./benchmark_mk_assoc.erl:7: function method2/1 undefined 109 | %% error 110 | %% 7> c(benchmark_mk_assoc). 111 | %% {ok,benchmark_mk_assoc} 112 | %% 8> benchmark_mk_assoc:test(300000). 113 | %% [{list,dict,ets},{false,251,49}] 114 | %% benchmark_mk_assoc:test(400000). 115 | %% [{list,dict,ets},{false,false,59}] 116 | 117 | %% benchmark_mk_assoc:test(1000000). 118 | %% [{list,dict,ets},{false,false,80}] 119 | 120 | -------------------------------------------------------------------------------- /code/broadcast.erl: -------------------------------------------------------------------------------- 1 | 2 | 3 | % http://www-net.cs.umass.edu/cs653-1997/notes/ch3/ch3-1.html 4 | 5 | %% http://www.ibrado.com/sock-faq/ 6 | %% http://docs.sun.com/db?q=sockets&p=/doc/802-5886/6i9k5sgso&a=view 7 | 8 | %% socket FAQ 9 | 10 | %% Messages sent by datagram sockets can be broadcast to reach all 11 | %% of the hosts on an attached network. The network must support broadcast; 12 | %% the system provides no simulation of broadcast in software. 13 | %% Broadcast messages can place a high load on a network since they 14 | %% force every host on the network to service them. 15 | %% Broadcasting is usually used for either of two reasons: to find 16 | %% a resource on a local network without having its address, 17 | %% or functions like routing require that information be sent to all 18 | %% accessible neighbors. 19 | %% 20 | %% To send a broadcast message, create an Internet datagram socket: 21 | %% 22 | %% s = socket(AF_INET, SOCK_DGRAM, 0); 23 | %% 24 | %% and bind a port number to the socket: 25 | %% 26 | %% sin.sin_family = AF_INET; 27 | %% sin.sin_addr.s_addr = htonl(INADDR_ANY); 28 | %% sin.sin_port = htons(MYPORT); 29 | %% bind(s, (struct sockaddr *) &sin, sizeof sin); 30 | %% 31 | %% The datagram can be broadcast on only one network by sending 32 | %% to the network's broadcast address. A datagram can also be broadcast 33 | %% on all attached networks by sending to the special address 34 | %% INADDR_BROADCAST, defined in . 35 | 36 | 37 | -module(broadcast). 38 | -compile(export_all). 39 | 40 | send(IoList) -> 41 | case inet:ifget("eth0", [broadaddr]) of 42 | {ok, [{broadaddr, Ip}]} -> 43 | {ok, S} = gen_udp:open(5010, [{broadcast, true}]), 44 | gen_udp:send(S, Ip, 6000, IoList), 45 | gen_udp:close(S); 46 | _ -> 47 | io:format("Bad interface name, or\n" 48 | "broadcasting not supported\n") 49 | end. 50 | 51 | listen() -> 52 | {ok, _} = gen_udp:open(6000), 53 | loop(). 54 | 55 | loop() -> 56 | receive 57 | Any -> 58 | io:format("received:~p~n", [Any]), 59 | loop() 60 | end. 61 | 62 | 63 | -------------------------------------------------------------------------------- /code/chat_cluster.erl: -------------------------------------------------------------------------------- 1 | -module(chat_cluster). 2 | 3 | -export([start/1, remote/2]). 4 | 5 | start(Node) -> 6 | spawn(fun() -> chatter(Node) end). 7 | 8 | chatter(Node) -> 9 | S = self(), 10 | Local = io_widget:start(S), 11 | io_widget:set_title(Local, "chat to " ++ atom_to_list(Node)), 12 | Remote = rpc:call(Node, chat, remote, [node(), Local]), 13 | loop(Local, Remote). 14 | 15 | remote(RemoteNode, Remote) -> 16 | S = self(), 17 | spawn(fun() -> remote1(S, RemoteNode, Remote) end), 18 | receive 19 | Pid -> Pid 20 | end. 21 | 22 | remote1(Parent, RemoteNode, Remote) -> 23 | Local = io_widget:start(self()), 24 | io_widget:set_title(Local, "chat to " ++ atom_to_list(RemoteNode)), 25 | Parent ! Local, 26 | loop(Local, Remote). 27 | 28 | 29 | loop(A, B) -> 30 | receive 31 | {_, {data, Str}} -> 32 | io_widget:insert(A, [$\n|Str]), 33 | io_widget:insert(B, [$\n|Str]), 34 | loop(A, B); 35 | Any -> 36 | io:format("~p~n", [Any]), 37 | loop(A, B) 38 | end. 39 | 40 | -------------------------------------------------------------------------------- /code/chat_file_transfer.erl: -------------------------------------------------------------------------------- 1 | -module(chat_file_transfer). 2 | 3 | -compile(export_all). 4 | 5 | %% server: 6 | %% chat_socket:start_chat_server("joe", "123"). 7 | %% "joe" is who I am 8 | %% 9 | %% client: 10 | %% chat_socket:call("localhost","fred", "123"). 11 | %% "fred" is who I am 12 | %% When we call we don't know the name of who we are calling 13 | %% so we'll have to wait until the server tells us 14 | 15 | 16 | %% Secret = none | string 17 | %% 18 | %% Who = the person who owns the server 19 | 20 | 21 | start_chat_server(Who, Secret) -> 22 | lib_auth_cs:start_server(3030, 23 | {?MODULE, chat_server, Who}, 24 | Secret, 25 | {yes, server}, 26 | 50). 27 | 28 | stop_echo_server() -> 29 | new_server:stop(3030). 30 | 31 | %% The client is the initiator 32 | %% Example call("localhost", "joe", "foo123") 33 | 34 | call(Host, From, Secret) -> 35 | lib_auth_cs:start_auth_client(Host, 36 | 3030, 37 | {?MODULE, chat_client, {Host, From}}, 38 | Secret, 39 | {yes, client}). 40 | 41 | 42 | 43 | %%---------------------------------------------------------------------- 44 | 45 | chat_server(Client, Me) -> 46 | %% when we are started popup an I/O widget 47 | Widget = io_widget:start(self()), 48 | receive 49 | {msg, {hello, From}} -> 50 | Client ! {msg, {hello, Me}}, 51 | io_widget:set_title(Widget, From), 52 | relay(Client, Widget, Me) 53 | end. 54 | 55 | relay(Pid, Widget, Who) -> 56 | receive 57 | {msg, {tell, Str}} -> 58 | io_widget:insert(Widget, Str), 59 | relay(Pid, Widget, Who); 60 | closed -> 61 | void; 62 | {Widget, {data, Str}} -> 63 | Str1 = Who ++ ":" ++ Str ++ "\n", 64 | io_widget:insert(Widget, Str1), 65 | Pid ! {msg, {tell, Str1}}, 66 | relay(Pid, Widget, Who); 67 | _Other -> 68 | relay(Pid, Widget, Who) 69 | end. 70 | 71 | 72 | 73 | chat_client(Server, {_Host, From}) -> 74 | Widget = io_widget:start(self()), 75 | Server ! {msg, {hello, From}}, 76 | receive 77 | {msg, {hello, Me}} -> 78 | io_widget:set_title(Widget, Me), 79 | relay(Server, Widget, From) 80 | end. 81 | 82 | 83 | -------------------------------------------------------------------------------- /code/chat_multi.erl: -------------------------------------------------------------------------------- 1 | -module(chat_multi). 2 | 3 | -compile(export_all). 4 | 5 | %% server: 6 | %% chat_socket:start_chat_server("joe", "123"). 7 | %% "joe" is who I am 8 | %% 9 | %% client: 10 | %% chat_socket:call("localhost","fred", "123"). 11 | %% "fred" is who I am 12 | %% When we call we don't know the name of who we are calling 13 | %% so we'll have to wait until the server tells us 14 | 15 | 16 | %% Secret = none | string 17 | %% 18 | %% Who = the person who owns the server 19 | 20 | 21 | start_chat_server(Who, Secret) -> 22 | lib_auth_cs:start_server(3030, 23 | {?MODULE, chat_server, Who}, 24 | Secret, 25 | {yes, server}, 26 | 50). 27 | 28 | stop_echo_server() -> 29 | new_server:stop(3030). 30 | 31 | %% The client is the initiator 32 | %% Example call("localhost", "joe", "foo123") 33 | 34 | call(Host, From, Secret) -> 35 | lib_auth_cs:start_auth_client(Host, 36 | 3030, 37 | {?MODULE, chat_client, {Host, From}}, 38 | Secret, 39 | {yes, client}). 40 | 41 | 42 | 43 | %%---------------------------------------------------------------------- 44 | 45 | chat_server(Client, Me) -> 46 | %% when we are started popup an I/O widget 47 | Widget = io_widget:start(self()), 48 | receive 49 | {msg, {hello, From}} -> 50 | Client ! {msg, {hello, Me}}, 51 | io_widget:set_title(Widget, From), 52 | relay(Client, Widget, Me) 53 | end. 54 | 55 | relay(Pid, Widget, Who) -> 56 | receive 57 | {msg, {tell, Str}} -> 58 | io_widget:insert(Widget, Str), 59 | relay(Pid, Widget, Who); 60 | closed -> 61 | void; 62 | {Widget, {data, Str}} -> 63 | Str1 = Who ++ ":" ++ Str ++ "\n", 64 | io_widget:insert(Widget, Str1), 65 | Pid ! {msg, {tell, Str1}}, 66 | relay(Pid, Widget, Who); 67 | _Other -> 68 | relay(Pid, Widget, Who) 69 | end. 70 | 71 | 72 | 73 | chat_client(Server, {_Host, From}) -> 74 | Widget = io_widget:start(self()), 75 | Server ! {msg, {hello, From}}, 76 | receive 77 | {msg, {hello, Me}} -> 78 | io_widget:set_title(Widget, Me), 79 | relay(Server, Widget, From) 80 | end. 81 | 82 | 83 | -------------------------------------------------------------------------------- /code/chat_secure.erl: -------------------------------------------------------------------------------- 1 | -module(chat_secure). 2 | 3 | -compile(export_all). 4 | 5 | %% server: 6 | %% chat_socket:start_chat_server("joe", "123"). 7 | %% "joe" is who I am 8 | %% 9 | %% client: 10 | %% chat_socket:call("localhost","fred", "123"). 11 | %% "fred" is who I am 12 | %% When we call we don't know the name of who we are calling 13 | %% so we'll have to wait until the server tells us 14 | 15 | 16 | %% Secret = none | string 17 | %% 18 | %% Who = the person who owns the server 19 | 20 | 21 | start_chat_server(Who, Secret) -> 22 | lib_auth_cs:start_server(3030, 23 | {?MODULE, chat_server, Who}, 24 | Secret, 25 | {yes, server}, 26 | 50). 27 | 28 | stop_echo_server() -> 29 | new_server:stop(3030). 30 | 31 | %% The client is the initiator 32 | %% Example call("localhost", "joe", "foo123") 33 | 34 | call(Host, From, Secret) -> 35 | lib_auth_cs:start_auth_client(Host, 36 | 3030, 37 | {?MODULE, chat_client, {Host, From}}, 38 | Secret, 39 | {yes, client}). 40 | 41 | 42 | 43 | %%---------------------------------------------------------------------- 44 | 45 | chat_server(Client, Me) -> 46 | %% when we are started popup an I/O widget 47 | Widget = io_widget:start(self()), 48 | receive 49 | {msg, {hello, From}} -> 50 | Client ! {msg, {hello, Me}}, 51 | io_widget:set_title(Widget, From), 52 | relay(Client, Widget, Me) 53 | end. 54 | 55 | relay(Pid, Widget, Who) -> 56 | receive 57 | {msg, {tell, Str}} -> 58 | io_widget:insert(Widget, Str), 59 | relay(Pid, Widget, Who); 60 | closed -> 61 | void; 62 | {Widget, {data, Str}} -> 63 | Str1 = Who ++ ":" ++ Str ++ "\n", 64 | io_widget:insert(Widget, Str1), 65 | Pid ! {msg, {tell, Str1}}, 66 | relay(Pid, Widget, Who); 67 | _Other -> 68 | relay(Pid, Widget, Who) 69 | end. 70 | 71 | 72 | 73 | chat_client(Server, {_Host, From}) -> 74 | Widget = io_widget:start(self()), 75 | Server ! {msg, {hello, From}}, 76 | receive 77 | {msg, {hello, Me}} -> 78 | io_widget:set_title(Widget, Me), 79 | relay(Server, Widget, From) 80 | end. 81 | 82 | 83 | -------------------------------------------------------------------------------- /code/chat_socket.erl: -------------------------------------------------------------------------------- 1 | -module(chat_socket). 2 | 3 | -compile(export_all). 4 | 5 | %% server: 6 | %% chat_socket:start_chat_server("joe", "123"). 7 | %% "joe" is who I am 8 | %% 9 | %% client: 10 | %% chat_socket:call("localhost","fred", "123"). 11 | %% "fred" is who I am 12 | %% When we call we don't know the name of who we are calling 13 | %% so we'll have to wait until the server tells us 14 | 15 | 16 | %% Secret = none | string 17 | %% 18 | %% Who = the person who owns the server 19 | 20 | 21 | start_chat_server(Who, Secret) -> 22 | lib_auth_cs:start_server(3030, 23 | {?MODULE, chat_server, Who}, 24 | Secret, 25 | {yes, server}, 26 | 50). 27 | 28 | stop_echo_server() -> 29 | new_server:stop(3030). 30 | 31 | %% The client is the initiator 32 | %% Example call("localhost", "joe", "foo123") 33 | 34 | call(Host, From, Secret) -> 35 | lib_auth_cs:start_auth_client(Host, 36 | 3030, 37 | {?MODULE, chat_client, {Host, From}}, 38 | Secret, 39 | {yes, client}). 40 | 41 | 42 | 43 | %%---------------------------------------------------------------------- 44 | 45 | chat_server(Client, Me) -> 46 | %% when we are started popup an I/O widget 47 | Widget = io_widget:start(self()), 48 | receive 49 | {msg, {hello, From}} -> 50 | Client ! {msg, {hello, Me}}, 51 | io_widget:set_title(Widget, From), 52 | relay(Client, Widget, Me) 53 | end. 54 | 55 | relay(Pid, Widget, Who) -> 56 | receive 57 | {msg, {tell, Str}} -> 58 | io_widget:insert(Widget, Str), 59 | relay(Pid, Widget, Who); 60 | closed -> 61 | void; 62 | {Widget, {data, Str}} -> 63 | Str1 = Who ++ ":" ++ Str ++ "\n", 64 | io_widget:insert(Widget, Str1), 65 | Pid ! {msg, {tell, Str1}}, 66 | relay(Pid, Widget, Who); 67 | _Other -> 68 | relay(Pid, Widget, Who) 69 | end. 70 | 71 | 72 | 73 | chat_client(Server, {_Host, From}) -> 74 | Widget = io_widget:start(self()), 75 | Server ! {msg, {hello, From}}, 76 | receive 77 | {msg, {hello, Me}} -> 78 | io_widget:set_title(Widget, Me), 79 | relay(Server, Widget, From) 80 | end. 81 | 82 | 83 | -------------------------------------------------------------------------------- /code/checker.erl: -------------------------------------------------------------------------------- 1 | -module(checker). 2 | 3 | -compile(export_all). 4 | -import(lists, [all/2, foreach/2, member/2]). 5 | 6 | %% checker:start() 7 | %% checks consistency of names 8 | 9 | start() -> 10 | F = lib_find:files(".", "*.erl", true), 11 | foreach(fun(I) -> check(I) end, F). 12 | 13 | test() -> 14 | check("./checker.erl"). 15 | 16 | check(File) -> 17 | io:format("Check:~p~n",[File]), 18 | case epp:parse_file(File, ".", []) of 19 | {ok, Forms} -> 20 | chk(File, Forms); 21 | _ -> 22 | true 23 | end. 24 | 25 | chk(File, F) -> 26 | try do(F) 27 | catch 28 | _:W -> 29 | io:format("File:~p ~p~n",[File,W]) 30 | end. 31 | 32 | do({function,_,Name,Arity,Clauses}) -> 33 | %% io:format("checfun ~p~n",[Name]), 34 | isFuname(Name), 35 | do(Clauses); 36 | do({call,_,{remote,_,A,B},Args}) -> 37 | is_valid_function_name(A), 38 | is_valid_function_name(B), 39 | do(Args); 40 | do({call,_,F,Args}) -> 41 | is_valid_function_name(F), 42 | do(Args); 43 | do({var,_,V}) -> 44 | is_valid_variable_name(V); 45 | do(T) when tuple(T) -> 46 | foreach(fun do/1, tuple_to_list(T)); 47 | do(T) when list(T) -> 48 | foreach(fun do/1, T); 49 | do(_) -> 50 | true. 51 | 52 | is_valid_function_name({atom,_,N}) -> 53 | isFuname(N); 54 | is_valid_function_name(X) -> 55 | do(X). 56 | 57 | isFuname(N) -> 58 | %% io:format("Checking:~p~n",[N]), 59 | case is_valid_funname(atom_to_list(N)) of 60 | false -> io:format("** invalid function name:~p~n",[N]); 61 | _ -> void 62 | end. 63 | 64 | -include("stdmacros.hrl"). 65 | 66 | is_valid_funname([H1,H2|T]) when ?IN(H1,$a,$z), 67 | ?IN(H2,$A,$Z) -> 68 | false; 69 | is_valid_funname([_|T]) -> 70 | is_valid_funname(T); 71 | is_valid_funname([]) -> 72 | true. 73 | 74 | is_valid_variable_name(V_n) -> 75 | L = atom_to_list(V_n), 76 | case L of 77 | "_" ++ T -> 78 | true; 79 | _ -> 80 | case (not member($_,L)) of 81 | false -> io:format("** invalid variable name:~p~n",[L]); 82 | _ -> void 83 | end 84 | end. 85 | 86 | is_fun_name_char($_) -> true; 87 | is_fun_name_char(X) when $a =< X, X =< $z -> true; 88 | is_fun_name_char(_) -> false. 89 | 90 | doIt(A) -> 91 | a. 92 | 93 | -------------------------------------------------------------------------------- /code/clock.erl: -------------------------------------------------------------------------------- 1 | -module(clock). 2 | -export([start/2, stop/0]). 3 | 4 | start(Time, Fun) -> 5 | register(clock, spawn(fun() -> tick(Time, Fun) end)). 6 | 7 | stop() -> clock ! stop. 8 | 9 | tick(Time, Fun) -> 10 | receive 11 | stop -> 12 | void 13 | after Time -> 14 | Fun(), 15 | tick(Time, Fun) 16 | end. 17 | -------------------------------------------------------------------------------- /code/convert1.erl: -------------------------------------------------------------------------------- 1 | -module(convert1). %% (1) 2 | -export([yards_to_meters/1, meters_to_yards/1]). %% (2) 3 | 4 | yards_to_meters(Yards) -> %% (3) 5 | 0.9144 * Yards. 6 | 7 | meters_to_yards(Meters) -> 8 | 1.0936133 * Meters. 9 | -------------------------------------------------------------------------------- /code/convert2.erl: -------------------------------------------------------------------------------- 1 | -module(convert2). 2 | 3 | -export([convert/2]). 4 | 5 | convert(Yards, meters) -> 0.9144 * Yards; 6 | convert(Meters, yards) -> 1.0936133 * Meters. 7 | -------------------------------------------------------------------------------- /code/convert3.erl: -------------------------------------------------------------------------------- 1 | -module(convert3). 2 | 3 | -export([convert/2]). 4 | 5 | convert({yards, X}, meters) -> {meters, 0.9144 * X}; 6 | convert({meters, X}, yards) -> {yards, 1.0936133 * X}. 7 | 8 | -------------------------------------------------------------------------------- /code/convert4.erl: -------------------------------------------------------------------------------- 1 | -module(convert4). 2 | 3 | -export([convert/2]). 4 | 5 | convert({yards, X}, meters) -> {meters, 0.9144 * X}; 6 | convert({meters, X}, yards) -> {yards, 1.0936133 * X}; 7 | convert({Tag, X}, Tag) -> {Tag, X}. 8 | 9 | 10 | -------------------------------------------------------------------------------- /code/convert5.erl: -------------------------------------------------------------------------------- 1 | -module(convert5). 2 | %% I can remove thjis it's not in the book 3 | -export([convert1/2, convert2/2]). 4 | 5 | 6 | convert1({yards, X}, meters) -> {meters, 0.9144 * X}; 7 | convert1({yards, X}, feet) -> {feet, 3 * X}; 8 | convert1({meters, X}, yards) -> {yards, 1.0936133 * X}; 9 | convert1({meters, X}, feet) -> {feet, 3.2808399 * X}; 10 | convert1({feet, X}, meters) -> {meters, 0.3048 * X}; 11 | convert1({feet, X}, yards) -> {yards, 0.3333333 * X}; 12 | convert1({Tag, X}, Tag) -> {Tag, X}. 13 | 14 | 15 | 16 | convert2(In, Out) -> 17 | case In of 18 | {yards, X} -> 19 | case Out of 20 | meters -> {meters, 0.9144 * X}; 21 | feet -> {feet, 3 * X} 22 | end; 23 | {meters, X} -> 24 | case Out of 25 | yards -> {yards, 1.0936133 * X}; 26 | feet -> {meters, 0.3048 * X} 27 | end; 28 | {feet, X} -> 29 | case Out of 30 | meters -> {meters, 0.3048 * X}; 31 | yards -> {yards, 0.3333333 * X} 32 | end; 33 | {_Tag, Out} -> 34 | %% I dont understahd this ... 35 | In 36 | end. 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /code/cookbook_examples.erl: -------------------------------------------------------------------------------- 1 | -module(cookbook_examples). 2 | 3 | %% examples in the cookbook chapter 4 | %% not generally useful for anything else than 5 | %% understanding the cookbook chapter 6 | 7 | -compile(export_all). 8 | -import(lists, [foreach/2, map/2, reverse/1]). 9 | 10 | 11 | time(Fun) -> 12 | timer:tc(lib_cookbook, eval_fun, [Fun]). 13 | 14 | eval_fun(Fun) -> Fun(). 15 | 16 | 17 | 18 | fib1(0) -> 1; 19 | fib1(1) -> 1; 20 | fib1(N) -> fib1(N-1) + fib1(N-2). 21 | 22 | 23 | 24 | fib2(N) -> fib2(N, 1, 1). 25 | 26 | fib2(0, A, _) -> A; 27 | fib2(N, A, B) -> fib2(N-1, B, A+B). 28 | 29 | 30 | 31 | extract_people(Tree) -> 32 | extract_people(Tree, []). 33 | 34 | extract_people({person,_,_}=Person, L) -> 35 | [Person|L]; 36 | extract_people([H|T], L) -> 37 | L1 = extract_people(H, L), 38 | extract_people(T, L1); 39 | extract_people(Tuple, L) when tuple(Tuple) -> 40 | extract_people(tuple_to_list(Tuple), L); 41 | extract_people(_, L) -> 42 | L. 43 | 44 | 45 | 46 | swap_names({person,First,Last}) -> 47 | {person, Last, First}; 48 | swap_names([H|T]) -> 49 | [swap_names(H)|swap_names(T)]; 50 | swap_names(Tuple) when tuple(Tuple) -> 51 | L1 = tuple_to_list(Tuple), 52 | L2 = map(fun swap_names/1, L1), 53 | list_to_tuple(L2); 54 | swap_names(X) -> 55 | X. 56 | 57 | 58 | 59 | shopping_list() -> 60 | [["Eggs", 24, 1.2], 61 | ["Apples", 6, 2.1], 62 | ["Sausages", 5, 5.6], 63 | ["Fish", 3, 5.4]]. 64 | 65 | print_shopping_list() -> 66 | show_format("~p ~p ~p~n"), 67 | show_format("~s ~w ~f~n"), 68 | show_format("~7s ~2w ~4.2f~n"), 69 | show_format("~-7s ~2w ~3.1f~n"). 70 | 71 | show_format(Format) -> 72 | io:format(" ~s~n",[Format]), 73 | foreach(fun(Items) -> io:format(Format, Items) end, 74 | shopping_list()), 75 | io:format("~n"). 76 | 77 | 78 | 79 | test_data1() -> 80 | {hello, 123, 81 | [foo,{a,b,[{person,"Xambro", "Skoplangard"},[a,b]], 82 | def, {g,h,{i, [a, {person,"Zolab","Zinkeldoffle"},q]}}}]}. 83 | 84 | 85 | 86 | binary_file_access() -> 87 | File = "test.bin", 88 | file:write_file(File, [<<"12345abcde">>]), 89 | {ok, Initial} = file:read_file(File), 90 | {ok, FileHandle} = file:open(File, [binary,raw,read,write]), 91 | {ok, B1} = file:pread(FileHandle, 1, 2), 92 | {ok, [First,Last]} = file:pread(FileHandle, [{0,3},{7,3}]), 93 | file:pwrite(FileHandle, [{0,Last},{7,First}]), 94 | file:close(FileHandle), 95 | {ok, Final} = file:read_file(File), 96 | {{initial,Initial}, 97 | {b1,B1}, 98 | {first,First}, {last,Last}, {final,Final}}. 99 | 100 | -------------------------------------------------------------------------------- /code/counter1.erl: -------------------------------------------------------------------------------- 1 | -module(counter1). 2 | -export([start/0, counter/1]). 3 | 4 | start() -> 5 | register(counter1, 6 | spawn(fun() -> 7 | io:format("Hello I'm a counter:~p~n",[self()]), 8 | counter(0) 9 | end)). 10 | 11 | counter(N) -> 12 | receive 13 | bump -> 14 | io:format("info=~p~n",[process_info(self())]), 15 | counter(N+1); 16 | display -> 17 | io:format("Counter ~p count=~p~n", [self(), N]), 18 | counter(N); 19 | die -> 20 | io:format("I'm going to die:~p~n",[self()]), 21 | 1/0, 22 | counter(N) 23 | end. 24 | 25 | -------------------------------------------------------------------------------- /code/counter2.erl: -------------------------------------------------------------------------------- 1 | -module(counter2). 2 | -export([start/0, read/1]). 3 | 4 | start() -> spawn(fun() -> counter(0) end). %% (1) 5 | 6 | read(Pid) -> %% (2) 7 | Pid ! {self(), read}, 8 | receive 9 | {Pid, N} -> 10 | N 11 | end. 12 | 13 | counter(N) -> 14 | receive 15 | bump -> 16 | counter(N+1); 17 | {From, read} -> %% (3) 18 | From ! {self(), N}, 19 | counter(N); 20 | stop -> 21 | true 22 | end. 23 | 24 | -------------------------------------------------------------------------------- /code/counter3.erl: -------------------------------------------------------------------------------- 1 | -module(counter3). 2 | 3 | -export([start/0, bump/1, read/1]). 4 | 5 | start() -> spawn(fun() -> counter(0) end). %% (1) 6 | 7 | bump(Pid) -> Pid ! bump. %% (2) 8 | 9 | 10 | 11 | read(Pid) -> 12 | Pid ! {self(), read}, 13 | receive 14 | {Pid, N} -> 15 | N 16 | after 17 | 1000 -> 18 | noreply 19 | end. 20 | 21 | 22 | counter(N) -> 23 | receive 24 | bump -> 25 | counter(N+1); 26 | {From, read} -> %% (3) 27 | From ! {self(), N}, %% (4) 28 | counter(N); 29 | stop -> 30 | true 31 | end. 32 | 33 | -------------------------------------------------------------------------------- /code/counter4.erl: -------------------------------------------------------------------------------- 1 | -module(counter4). 2 | -export([start/0]). 3 | 4 | start() -> lib_misc:spawn_monitor(counter, true, fun() -> counter(0) end). 5 | 6 | counter(N) -> 7 | receive 8 | bump -> 9 | counter(N+1); 10 | display -> 11 | io:format("Count=~p~n", [N]), 12 | counter(N); 13 | stop -> 14 | true 15 | end. 16 | -------------------------------------------------------------------------------- /code/ctemplate.erl: -------------------------------------------------------------------------------- 1 | -module(ctemplate). 2 | -compile(export_all). 3 | 4 | start() -> 5 | spawn(fun() -> loop([]) end). 6 | 7 | rpc(Pid, Request) -> 8 | Pid ! {self(), Request}, 9 | receive 10 | {Pid, Response} -> 11 | Response 12 | end. 13 | 14 | loop(X) -> 15 | receive 16 | Any -> 17 | io:format("Received:~p~n",[Any]), 18 | loop(X) 19 | end. 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /code/data1.dat: -------------------------------------------------------------------------------- 1 | {person, "joe", "armstrong", 2 | [{occupation, programmer}, 3 | {favoriteLanguage, erlang}]}. 4 | 5 | {cat, {name, "zorro"}, 6 | {owner, "joe"}}. 7 | -------------------------------------------------------------------------------- /code/dist_demo.erl: -------------------------------------------------------------------------------- 1 | -module(dist_demo). 2 | -export([rpc/4, start/1]). 3 | 4 | start(Node) -> 5 | spawn(Node, fun() -> loop() end). 6 | 7 | rpc(Pid, M, F, A) -> 8 | Pid ! {rpc, self(), M, F, A}, 9 | receive 10 | {Pid, Response} -> 11 | Response 12 | end. 13 | 14 | loop() -> 15 | receive 16 | {rpc, Pid, M, F, A} -> 17 | Pid ! {self(), (catch apply(M, F, A))}, 18 | loop() 19 | end. 20 | -------------------------------------------------------------------------------- /code/edemo1.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(edemo1). 3 | -export([start/2]). 4 | 5 | start(Bool, M) -> 6 | A = spawn(fun() -> a() end), 7 | B = spawn(fun() -> b(A, Bool) end), 8 | C = spawn(fun() -> c(B, M) end), 9 | sleep(1000), 10 | status(b, B), 11 | status(c, C). 12 | 13 | 14 | a() -> 15 | process_flag(trap_exit, true), 16 | wait(a). 17 | 18 | b(A, Bool) -> 19 | process_flag(trap_exit, Bool), 20 | link(A), 21 | wait(b). 22 | 23 | c(B, M) -> 24 | link(B), 25 | case M of 26 | {die, Reason} -> 27 | exit(Reason); 28 | {divide, N} -> 29 | 1/N, 30 | wait(c); 31 | normal -> 32 | true 33 | end. 34 | 35 | 36 | 37 | wait(Prog) -> 38 | receive 39 | Any -> 40 | io:format("Process ~p received ~p~n",[Prog, Any]), 41 | wait(Prog) 42 | end. 43 | 44 | 45 | 46 | sleep(T) -> 47 | receive 48 | after T -> true 49 | end. 50 | 51 | status(Name, Pid) -> 52 | case erlang:is_process_alive(Pid) of 53 | true -> 54 | io:format("process ~p (~p) is alive~n", [Name, Pid]); 55 | false -> 56 | io:format("process ~p (~p) is dead~n", [Name,Pid]) 57 | end. 58 | 59 | 60 | %% 3> edemo1:start(false, {die, abc}). 61 | %% Process a received {'EXIT',<0.44.0>,abc} 62 | %% process a (<0.43.0>) is alive 63 | %% process b (<0.44.0>) is dead 64 | %% process c (<0.45.0>) is dead 65 | %% ok 66 | %% 4> edemo1:start(false, {die, normal}). 67 | %% process a (<0.47.0>) is alive 68 | %% process b (<0.48.0>) is alive 69 | %% process c (<0.49.0>) is dead 70 | %% ok 71 | %% 5> edemo1:start(false, {divide,0}). 72 | 73 | %% =ERROR REPORT==== 8-Dec-2006::11:12:47 === 74 | %% Error in process <0.53.0> with exit value: {badarith,[{edemo1,c,2}]} 75 | 76 | %% Process a received {'EXIT',<0.52.0>,{badarith,[{edemo1,c,2}]}} 77 | %% process a (<0.51.0>) is alive 78 | %% process b (<0.52.0>) is dead 79 | %% process c (<0.53.0>) is dead 80 | %% ok 81 | %% 6> edemo1:start(false, {die,kill}). 82 | %% Process a received {'EXIT',<0.56.0>,killed} <-- ** changed to killed *** 83 | %% process a (<0.55.0>) is alive 84 | %% process b (<0.56.0>) is dead 85 | %% process c (<0.57.0>) is dead 86 | %% ok 87 | 88 | 89 | %% 7> edemo1:start(true, {die, abc}). 90 | %% Process b received {'EXIT',<0.61.0>,abc} 91 | %% process a (<0.59.0>) is alive 92 | %% process b (<0.60.0>) is alive 93 | %% process c (<0.61.0>) is dead 94 | %% ok 95 | %% 8> edemo1:start(true, {die, normal}). 96 | %% Process b received {'EXIT',<0.65.0>,normal} 97 | %% process a (<0.63.0>) is alive 98 | %% process b (<0.64.0>) is alive 99 | %% process c (<0.65.0>) is dead 100 | %% ok 101 | %% 9> edemo1:start(true, normal). 102 | %% Process b received {'EXIT',<0.69.0>,normal} 103 | %% process a (<0.67.0>) is alive 104 | %% process b (<0.68.0>) is alive 105 | %% process c (<0.69.0>) is dead 106 | %% ok 107 | %% 10> edemo1:start(true, {die,kill}). 108 | %% Process b received {'EXIT',<0.73.0>,kill} 109 | %% process a (<0.71.0>) is alive 110 | %% process b (<0.72.0>) is alive 111 | %% process c (<0.73.0>) is dead 112 | %% ok 113 | %% 11> 114 | 115 | -------------------------------------------------------------------------------- /code/edemo2.erl: -------------------------------------------------------------------------------- 1 | -module(edemo2). 2 | -export([start/2]). 3 | 4 | %% test exit(B, Why) 5 | 6 | start(Bool, M) -> 7 | %% Spawn three process A B and C 8 | A = spawn(fun() -> a() end), 9 | B = spawn(fun() -> b(A, Bool) end), 10 | C = spawn(fun() -> c(B, M) end), 11 | sleep(1000), 12 | status(a, A), 13 | status(b, B), 14 | status(c, C). 15 | 16 | a() -> 17 | process_flag(trap_exit, true), 18 | wait(a). 19 | 20 | b(A, Bool) -> 21 | process_flag(trap_exit, Bool), 22 | link(A), 23 | wait(b). 24 | 25 | c(B, M) -> 26 | process_flag(trap_exit, true), 27 | link(B), 28 | exit(B, M), 29 | wait(c). 30 | 31 | 32 | wait(Prog) -> 33 | receive 34 | Any -> 35 | io:format("Process ~p received ~p~n",[Prog, Any]), 36 | wait(Prog) 37 | end. 38 | 39 | sleep(T) -> 40 | receive 41 | after T -> true 42 | end. 43 | 44 | status(Name, Pid) -> 45 | case process_info(Pid) of 46 | undefined -> 47 | io:format("process ~p (~p) is dead~n", [Name,Pid]); 48 | _ -> 49 | io:format("process ~p (~p) is alive~n", [Name, Pid]) 50 | end. 51 | 52 | %% 11> c(edemo2). 53 | %% {ok,edemo2} 54 | %% 12> edemo2:start(false, abc). 55 | %% Process c received {'EXIT',<0.81.0>,abc} 56 | %% Process a received {'EXIT',<0.81.0>,abc} 57 | %% process a (<0.80.0>) is alive 58 | %% process b (<0.81.0>) is dead 59 | %% process c (<0.82.0>) is alive 60 | %% ok 61 | %% 13> edemo2:start(false, normal). 62 | %% process a (<0.84.0>) is alive 63 | %% process b (<0.85.0>) is alive 64 | %% process c (<0.86.0>) is alive 65 | %% ok 66 | %% 14> edemo2:start(false, normal). 67 | %% process a (<0.88.0>) is alive 68 | %% process b (<0.89.0>) is alive 69 | %% process c (<0.90.0>) is alive 70 | %% ok 71 | %% 15> edemo2:start(false, die). 72 | %% Process c received {'EXIT',<0.93.0>,die} 73 | %% Process a received {'EXIT',<0.93.0>,die} 74 | %% process a (<0.92.0>) is alive 75 | %% process b (<0.93.0>) is dead 76 | %% process c (<0.94.0>) is alive 77 | %% ok 78 | %% 16> edemo2:start(false, kill). 79 | %% Process c received {'EXIT',<0.97.0>,killed} 80 | %% Process a received {'EXIT',<0.97.0>,killed} 81 | %% process a (<0.96.0>) is alive 82 | %% process b (<0.97.0>) is dead 83 | %% process c (<0.98.0>) is alive 84 | %% ok 85 | %% 17> edemo2:start(true, abc). 86 | %% Process b received {'EXIT',<0.102.0>,abc} 87 | %% process a (<0.100.0>) is alive 88 | %% process b (<0.101.0>) is alive 89 | %% process c (<0.102.0>) is alive 90 | %% ok 91 | %% 18> edemo2:start(true, normal). 92 | %% Process b received {'EXIT',<0.106.0>,normal} 93 | %% process a (<0.104.0>) is alive 94 | %% process b (<0.105.0>) is alive 95 | %% process c (<0.106.0>) is alive 96 | %% ok 97 | %% 19> edemo2:start(true, kill). 98 | %% Process c received {'EXIT',<0.109.0>,killed} 99 | %% Process a received {'EXIT',<0.109.0>,killed} 100 | %% process a (<0.108.0>) is alive 101 | %% process b (<0.109.0>) is dead 102 | %% process c (<0.110.0>) is alive 103 | %% ok 104 | -------------------------------------------------------------------------------- /code/elog1.config: -------------------------------------------------------------------------------- 1 | %% no tty 2 | [{sasl, [ 3 | {sasl_error_logger, false} 4 | ]}]. 5 | 6 | 7 | -------------------------------------------------------------------------------- /code/elog2.config: -------------------------------------------------------------------------------- 1 | %% single text file - minimal tty 2 | [{sasl, [ 3 | %% All reports go to this file 4 | {sasl_error_logger, {file, "/home/joe/error_logs/THELOG"}} 5 | ]}]. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /code/elog3.config: -------------------------------------------------------------------------------- 1 | %% rotating log and minimal tty 2 | [{sasl, [ 3 | {sasl_error_logger, false}, 4 | %% define the parameters of the rotating log 5 | %% the log file directory 6 | {error_logger_mf_dir,"/home/joe/error_logs"}, 7 | %% # bytes per logfile 8 | {error_logger_mf_maxbytes,10485760}, % 10 MB 9 | %% maximum number of logfiles 10 | {error_logger_mf_maxfiles, 10} 11 | ]}]. 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /code/elog4.config: -------------------------------------------------------------------------------- 1 | %% rotating log and errors 2 | [{sasl, [ 3 | %% minimise shell error logging 4 | {sasl_error_logger, false}, 5 | %% only report errors 6 | {errlog_type, error}, 7 | %% define the parameters of the rotating log 8 | %% the log file directory 9 | {error_logger_mf_dir,"/home/joe/error_logs"}, 10 | %% # bytes per logfile 11 | {error_logger_mf_maxbytes,10485760}, % 10 MB 12 | %% maximum number of 13 | {error_logger_mf_maxfiles, 10} 14 | ]}]. 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /code/elog5.config: -------------------------------------------------------------------------------- 1 | %% text error log 2 | [ {kernel, 3 | [{error_logger, 4 | {file, "/home/joe/error_logs/debug.log"}}]}]. 5 | -------------------------------------------------------------------------------- /code/emacs.setup: -------------------------------------------------------------------------------- 1 | (setq default-frame-alist 2 | '((top . 10) (left . 10) 3 | (width . 80) (height . 43) 4 | (cursor-color . "blue") 5 | (cursor-type . box) 6 | (foreground-color . "black") 7 | (background-color . "white") 8 | (font . "-*-Courier New-bold-r-*-*-18-108-120-120-c-*-iso8859-8"))) 9 | 10 | (show-paren-mode) 11 | 12 | (global-font-lock-mode t) 13 | (setq font-lock-maximum-decoration t) 14 | 15 | 16 | ;; Erlang stuff this is the path to erlang 17 | 18 | ;; windows path below -- change to match your environment 19 | (setq load-path (cons "c:/Program Files/erl5.5.3/lib/tools-2.5.3/emacs" 20 | load-path)) 21 | 22 | (require 'erlang-start) 23 | 24 | ;; (if window-system 25 | ;; (add-hook 'erlang-mode-hook 'erlang-font-lock-level-3)) 26 | 27 | (add-hook 'erlang-mode-hook 'erlang-font-lock-level-3) 28 | -------------------------------------------------------------------------------- /code/error1.erl: -------------------------------------------------------------------------------- 1 | -module(error1). 2 | -export([start/0]). 3 | 4 | start() -> loop(0). 5 | 6 | loop(M) -> 7 | io:format("error1 M=~p~n",[M]), 8 | receive 9 | K -> 10 | 1/K, 11 | loop(M+1) 12 | after 500 -> 13 | loop(M+1) 14 | end. 15 | -------------------------------------------------------------------------------- /code/ets_test.erl: -------------------------------------------------------------------------------- 1 | -module(ets_test). 2 | -export([start/0]). 3 | 4 | start() -> 5 | lists:foreach(fun test_ets/1, 6 | [set, ordered_set, bag, duplicate_bag]). 7 | 8 | test_ets(Mode) -> 9 | TableId = ets:new(test, [Mode]), 10 | ets:insert(TableId, {a,1}), 11 | ets:insert(TableId, {b,2}), 12 | ets:insert(TableId, {a,1}), 13 | ets:insert(TableId, {a,3}), 14 | List = ets:tab2list(TableId), 15 | io:format("~-13w => ~p~n", [Mode, List]), 16 | ets:delete(TableId). 17 | -------------------------------------------------------------------------------- /code/event_handler.erl: -------------------------------------------------------------------------------- 1 | -module(event_handler). 2 | -export([make/1, add_handler/2, event/2]). 3 | 4 | %% make a new event handler called Name 5 | %% the handler function is no_op -- so we do nothing with the event 6 | make(Name) -> 7 | register(Name, spawn(fun() -> my_handler(fun no_op/1) end)). 8 | 9 | add_handler(Name, Fun) -> Name ! {add, Fun}. 10 | 11 | %% generate an event 12 | event(Name, X) -> Name ! {event, X}. 13 | 14 | my_handler(Fun) -> 15 | receive 16 | {add, Fun1} -> 17 | my_handler(Fun1); 18 | {event, Any} -> 19 | (catch Fun(Any)), 20 | my_handler(Fun) 21 | end. 22 | 23 | no_op(_) -> void. 24 | 25 | -------------------------------------------------------------------------------- /code/exprs.dat: -------------------------------------------------------------------------------- 1 | X=1+2. 2 | 5*4+10. 3 | lists:reverse("esirprus esirprus"). 4 | Hypot = fun(X,Y) -> math:sqrt(X*X + Y*Y) end. 5 | Hypot(X,4). 6 | -------------------------------------------------------------------------------- /code/extract.erl: -------------------------------------------------------------------------------- 1 | -module(extract). 2 | -export([attribute/2]). 3 | 4 | attribute(File, Key) -> 5 | case beam_lib:chunks(File,[attributes]) of 6 | {ok, {_Module, [{attributes,L}]}} -> 7 | case lookup(Key, L) of 8 | {ok, Val} -> 9 | Val; 10 | error -> 11 | exit(badAttribute) 12 | end; 13 | _ -> 14 | exit(badFile) 15 | end. 16 | 17 | lookup(Key, [{Key,Val}|_]) -> {ok, Val}; 18 | lookup(Key, [_|T]) -> lookup(Key, T); 19 | lookup(_, []) -> error. 20 | -------------------------------------------------------------------------------- /code/fac.erl: -------------------------------------------------------------------------------- 1 | -module(fac). 2 | -export([fac/1]). 3 | 4 | fac(0) -> 1; 5 | fac(N) -> N*fac(N-1). 6 | 7 | -------------------------------------------------------------------------------- /code/fac1.erl: -------------------------------------------------------------------------------- 1 | -module(fac1). 2 | -export([main/1]). 3 | 4 | main([A]) -> 5 | I = list_to_integer(atom_to_list(A)), 6 | F = fac(I), 7 | io:format("factorial ~w = ~w~n",[I, F]), 8 | init:stop(). 9 | 10 | fac(0) -> 1; 11 | fac(N) -> N*fac(N-1). 12 | 13 | -------------------------------------------------------------------------------- /code/factorial: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | main([A]) -> 4 | I = list_to_integer(A), 5 | F = fac(I), 6 | io:format("factorial ~w = ~w~n",[I, F]). 7 | 8 | fac(0) -> 1; 9 | fac(N) -> 10 | N * fac(N-1). 11 | -------------------------------------------------------------------------------- /code/gathered.html: -------------------------------------------------------------------------------- 1 | 9 |

Urls

10 | 26 | -------------------------------------------------------------------------------- /code/gen_server_template.full: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% File : gen_server_template.full 3 | %%% Author : my name 4 | %%% Description : 5 | %%% 6 | %%% Created : 2 Mar 2007 by my name 7 | %%%------------------------------------------------------------------- 8 | -module(). 9 | 10 | -behaviour(gen_server). 11 | 12 | %% API 13 | -export([start_link/0]). 14 | 15 | %% gen_server callbacks 16 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 17 | terminate/2, code_change/3]). 18 | 19 | -record(state, {}). 20 | 21 | %%==================================================================== 22 | %% API 23 | %%==================================================================== 24 | %%-------------------------------------------------------------------- 25 | %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} 26 | %% Description: Starts the server 27 | %%-------------------------------------------------------------------- 28 | start_link() -> 29 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 30 | 31 | %%==================================================================== 32 | %% gen_server callbacks 33 | %%==================================================================== 34 | 35 | %%-------------------------------------------------------------------- 36 | %% Function: init(Args) -> {ok, State} | 37 | %% {ok, State, Timeout} | 38 | %% ignore | 39 | %% {stop, Reason} 40 | %% Description: Initiates the server 41 | %%-------------------------------------------------------------------- 42 | init([]) -> 43 | {ok, #state{}}. 44 | 45 | %%-------------------------------------------------------------------- 46 | %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | 47 | %% {reply, Reply, State, Timeout} | 48 | %% {noreply, State} | 49 | %% {noreply, State, Timeout} | 50 | %% {stop, Reason, Reply, State} | 51 | %% {stop, Reason, State} 52 | %% Description: Handling call messages 53 | %%-------------------------------------------------------------------- 54 | handle_call(_Request, _From, State) -> 55 | Reply = ok, 56 | {reply, Reply, State}. 57 | 58 | %%-------------------------------------------------------------------- 59 | %% Function: handle_cast(Msg, State) -> {noreply, State} | 60 | %% {noreply, State, Timeout} | 61 | %% {stop, Reason, State} 62 | %% Description: Handling cast messages 63 | %%-------------------------------------------------------------------- 64 | handle_cast(_Msg, State) -> 65 | {noreply, State}. 66 | 67 | %%-------------------------------------------------------------------- 68 | %% Function: handle_info(Info, State) -> {noreply, State} | 69 | %% {noreply, State, Timeout} | 70 | %% {stop, Reason, State} 71 | %% Description: Handling all non call/cast messages 72 | %%-------------------------------------------------------------------- 73 | handle_info(_Info, State) -> 74 | {noreply, State}. 75 | 76 | %%-------------------------------------------------------------------- 77 | %% Function: terminate(Reason, State) -> void() 78 | %% Description: This function is called by a gen_server when it is about to 79 | %% terminate. It should be the opposite of Module:init/1 and do any necessary 80 | %% cleaning up. When it returns, the gen_server terminates with Reason. 81 | %% The return value is ignored. 82 | %%-------------------------------------------------------------------- 83 | terminate(_Reason, _State) -> 84 | ok. 85 | 86 | %%-------------------------------------------------------------------- 87 | %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} 88 | %% Description: Convert process state when code is changed 89 | %%-------------------------------------------------------------------- 90 | code_change(_OldVsn, State, _Extra) -> 91 | {ok, State}. 92 | 93 | %%-------------------------------------------------------------------- 94 | %%% Internal functions 95 | %%-------------------------------------------------------------------- 96 | -------------------------------------------------------------------------------- /code/gen_server_template.mini: -------------------------------------------------------------------------------- 1 | -module(). 2 | %% gen_server_mini_template 3 | 4 | -behaviour(gen_server). 5 | -export([start_link/0]). 6 | %% gen_server callbacks 7 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 8 | terminate/2, code_change/3]). 9 | 10 | start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 11 | 12 | init([]) -> {ok, State}. 13 | 14 | handle_call(_Request, _From, State) -> {reply, Reply, State}. 15 | handle_cast(_Msg, State) -> {noreply, State}. 16 | handle_info(_Info, State) -> {noreply, State}. 17 | terminate(_Reason, _State) -> ok. 18 | code_change(_OldVsn, State, Extra) -> {ok, State}. 19 | -------------------------------------------------------------------------------- /code/geometry.erl: -------------------------------------------------------------------------------- 1 | -module(geometry). 2 | -export([area/1]). 3 | 4 | area({rectangle, Width, Height}) -> Width * Height; 5 | area({circle, R}) -> 3.14159 * R * R. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /code/hello: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | main(_) -> 4 | io:format("Hello world\n"). 5 | -------------------------------------------------------------------------------- /code/hello.bat: -------------------------------------------------------------------------------- 1 | 2 | REMExcerpted from "Programming Erlang", 3 | REMpublished by The Pragmatic Bookshelf. 4 | REMCopyrights apply to this code. It may not be used to create training material, 5 | REMcourses, books, articles, and the like. Contact us if you are in doubt. 6 | REMWe make no guarantees that this code is fit for any purpose. 7 | REMVisit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information. 8 | 9 | "C:\Program Files\erl5.6.5\bin\erl.exe" -noshell -s hello start -s init stop 10 | -------------------------------------------------------------------------------- /code/hello.erl: -------------------------------------------------------------------------------- 1 | -module(hello). 2 | -export([start/0]). 3 | 4 | start() -> 5 | io:format("Hello world~n"). 6 | -------------------------------------------------------------------------------- /code/hello.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #--- 3 | # Excerpted from "Programming Erlang", 4 | # published by The Pragmatic Bookshelf. 5 | # Copyrights apply to this code. It may not be used to create training material, 6 | # courses, books, articles, and the like. Contact us if you are in doubt. 7 | # We make no guarantees that this code is fit for any purpose. 8 | # Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information. 9 | #--- 10 | erl -noshell -pa /home/joe/2009/book/JAERLANG/Book/code\ 11 | -s hello start -s init stop 12 | 13 | -------------------------------------------------------------------------------- /code/id3_tag_lengths.erl: -------------------------------------------------------------------------------- 1 | -module(id3_tag_lengths). 2 | 3 | %% Warning the code in this module uses a rather 4 | %% crude way of finding the start and stiop pointers 5 | %% into the music - for this reason it is not listed inm the book 6 | %% anybody who finds out how to do this correctly is 7 | %% welcome to tell the author how to do it. 8 | 9 | %% My strategy is rather rigid: 10 | %% use mp3_sync:find_sync(Bin, 1) to find the sync 11 | %% if this fails give up 12 | %% check the last 128 bytes if it says TAG then ok 13 | %% else give up 14 | %% make a half hearted attemp to analyse the ID3 tags at the begging 15 | %% if this differetn with the result of find_sync then beleive find sync 16 | %% 17 | 18 | %% The entire point is that I must believe the resuts 19 | %% {ok, Start, Stop} -- means the data is between Start..Stop ' 20 | %% and that I can believe these numbers 21 | -compile(export_all). 22 | -import(lists, [foreach/2, map/2]). 23 | 24 | test() -> 25 | Files = lib_find:files("/home/joe/music_keep", "/*.mp3", true), 26 | map(fun(F) -> file(F) end, Files). 27 | 28 | file(File) -> 29 | read_id3_tag(File). 30 | 31 | read_id3_tag(File) -> 32 | case file:open(File, [read,binary,raw]) of 33 | {ok, S} -> 34 | Size = filelib:file_size(File), 35 | Result = analyse(S, Size), 36 | file:close(S), 37 | Result; 38 | _Error -> 39 | error 40 | end. 41 | 42 | analyse(S, Size) -> 43 | case (catch analyse1(S, Size)) of 44 | {'EXIT', _Why} -> 45 | io:format("_Why=~p~n",[_Why]), 46 | error; 47 | StartStop -> 48 | StartStop 49 | end. 50 | 51 | analyse1(S, Size) -> 52 | {ok, Bin} = file:pread(S, 0, 10000), 53 | {ok, StartTrust} = mp3_sync:find_sync(Bin, 1), 54 | {Type, StartUntrust} = parse_start_tag(Bin), 55 | Stop = parse_end_tag(S, Size), 56 | if 57 | StartTrust == StartUntrust -> 58 | true; 59 | true -> 60 | io:format("** error in header code: real=~p Type=~p Val=~p~n", 61 | [StartTrust, Type, StartUntrust]) 62 | end, 63 | {StartTrust, Stop}. 64 | 65 | 66 | parse_start_tag(<<$I,$D,$3,3,0,_Unsync:1,_Extended:1,_Experimental:1, 67 | _:5,K:32,_/binary>>) -> 68 | Tag = "ID3v2.3.0", 69 | Size = syncsafe2int(K), 70 | {Tag, Size+10}; 71 | parse_start_tag(<<$I,$D,$3,4,0,_Unsync:1,_Extended:1,_Experimental:1, 72 | Footer:1,_:4,K:32,_/binary>>) -> 73 | Tag = "ID3v2.3.0", 74 | Size = syncsafe2int(K), 75 | Size1 = case Footer of 76 | 1 -> 10 + Size; 77 | 0 -> Size 78 | end, 79 | {Tag, Size1+1}; 80 | parse_start_tag(<>) -> 81 | io:format("strange start tag~p~n",[X]), 82 | {error, 1}. 83 | 84 | parse_end_tag(S, Size) -> 85 | {ok, B2} = file:pread(S, Size-128, 128), 86 | parse_v1_tag(B2, Size). 87 | 88 | parse_v1_tag(<<$T,$A,$G,_/binary>>, Size) -> 89 | Size - 128; 90 | parse_v1_tag(_, Size) -> 91 | Size. 92 | 93 | syncsafe2int(N) -> 94 | <<_:1,N1:7,_:1,N2:7,_:1,N3:7,_:1,N4:7>> = <>, 95 | <> = <<0:4,N1:7,N2:7,N3:7,N4:7>>, 96 | I. 97 | -------------------------------------------------------------------------------- /code/id3_v1.erl: -------------------------------------------------------------------------------- 1 | -module(id3_v1). 2 | -import(lists, [filter/2, map/2, reverse/1]). 3 | -export([test/0, dir/1, read_id3_tag/1]). 4 | 5 | test() -> dir("/home/joe/music_keep"). 6 | 7 | dir(Dir) -> 8 | Files = lib_find:files(Dir, "*.mp3", true), 9 | L1 = map(fun(I) -> 10 | {I, (catch read_id3_tag(I))} 11 | end, Files), 12 | %% L1 = [{File, Parse}] where Parse = error | [{Tag,Val}] 13 | %% we now have to remove all the entries from L where 14 | %% Parse = error. We can do this with a filter operation 15 | L2 = filter(fun({_,error}) -> false; 16 | (_) -> true 17 | end, L1), 18 | lib_misc:dump("mp3data", L2). 19 | 20 | read_id3_tag(File) -> 21 | case file:open(File, [read,binary,raw]) of 22 | {ok, S} -> 23 | Size = filelib:file_size(File), 24 | {ok, B2} = file:pread(S, Size-128, 128), 25 | Result = parse_v1_tag(B2), 26 | file:close(S), 27 | Result; 28 | _Error -> 29 | error 30 | end. 31 | 32 | parse_v1_tag(<<$T,$A,$G, 33 | Title:30/binary, Artist:30/binary, 34 | Album:30/binary, _Year:4/binary, 35 | _Comment:28/binary, 0:8,Track:8,_Genre:8>>) -> 36 | {"ID3v1.1", 37 | [{track,Track}, {title,trim(Title)}, 38 | {artist,trim(Artist)}, {album, trim(Album)}]}; 39 | parse_v1_tag(<<$T,$A,$G, 40 | Title:30/binary, Artist:30/binary, 41 | Album:30/binary, _Year:4/binary, 42 | _Comment:30/binary,_Genre:8>>) -> 43 | {"ID3v1", 44 | [{title,trim(Title)}, 45 | {artist,trim(Artist)}, {album, trim(Album)}]}; 46 | parse_v1_tag(_) -> 47 | error. 48 | 49 | trim(Bin) -> 50 | list_to_binary(trim_blanks(binary_to_list(Bin))). 51 | 52 | trim_blanks(X) -> reverse(skip_blanks_and_zero(reverse(X))). 53 | 54 | skip_blanks_and_zero([$\s|T]) -> skip_blanks_and_zero(T); 55 | skip_blanks_and_zero([0|T]) -> skip_blanks_and_zero(T); 56 | skip_blanks_and_zero(X) -> X. 57 | -------------------------------------------------------------------------------- /code/indexer-1.1/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .erl .beam .yrl 2 | 3 | .erl.beam: 4 | erlc -W $< 5 | 6 | MODS = indexer_checkpoint indexer_filenames_dets indexer_porter\ 7 | indexer_words indexer_dir_crawler indexer_server\ 8 | indexer indexer_misc indexer_trigrams 9 | 10 | ERL = erl -boot start_clean 11 | 12 | compile: ${MODS:%=%.beam} trigramsS.tab 13 | @echo "make clean - clean up" 14 | 15 | all: compile 16 | 17 | trigramsS.tab: ../354984si.ngl.gz indexer_trigrams.beam 18 | @erl -noshell -boot start_clean -s indexer_trigrams make_tables\ 19 | -s init stop 20 | 21 | clean: 22 | rm -rf *.beam erl_crash.dump 23 | rm -rf trigramsS.tab 24 | 25 | -------------------------------------------------------------------------------- /code/indexer-1.1/Readme: -------------------------------------------------------------------------------- 1 | %% This directory contains all the code for the 2 | %% indexing program described in chapter 3 | %% Of Programming Erlang 4 | %% It makes use of one file outside the diectory 5 | %% ../354984si.ngl.gz 6 | 7 | -------------------------------------------------------------------------------- /code/indexer-1.1/indexer.erl: -------------------------------------------------------------------------------- 1 | -module(indexer). 2 | -export([start/0, stop/0, search/1, cold_start/0]). 3 | 4 | -import(lists, [map/2]). 5 | 6 | %% indexer:cold_start(). 7 | %% indexer:start(). 8 | %% indexer:stop(). 9 | %% indexer:search(Str) -> [File] 10 | 11 | cold_start() -> 12 | indexer_server:cold_start(output_dir(), dirs_to_index()). 13 | 14 | %% Note /home/joe/bigIndex Must be created first 15 | 16 | output_dir() -> "/home/joe/bigIndex". 17 | dirs_to_index() -> ["/home/joe/pre2003/erl.supported"]. 18 | 19 | 20 | start() -> 21 | indexer_server:start(output_dir()), 22 | spawn_link(fun() -> worker() end). 23 | 24 | 25 | search(Str) -> 26 | indexer_server:search(Str). 27 | 28 | stop() -> 29 | io:format("Scheduling a stop~n"), 30 | indexer_server:schedule_stop(). 31 | 32 | 33 | worker() -> 34 | possibly_stop(), 35 | case indexer_server:next_dir() of 36 | {ok, Dir} -> 37 | Files = indexer_misc:files_in_dir(Dir), 38 | index_these_files(Files), 39 | indexer_server:checkpoint(), 40 | possibly_stop(), 41 | sleep(10000), 42 | worker(); 43 | done -> 44 | true 45 | end. 46 | 47 | 48 | possibly_stop() -> 49 | case indexer_server:should_i_stop() of 50 | true -> 51 | io:format("Stopping~n"), 52 | indexer_server:stop(), 53 | exit(stopped); 54 | false -> 55 | void 56 | end. 57 | 58 | 59 | index_these_files(Files) -> 60 | Ets = indexer_server:ets_table(), 61 | OutDir = filename:join(indexer_server:outdir(), "index"), 62 | F1 = fun(Pid, File) -> indexer_words:words_in_file(Pid, File, Ets) end, 63 | F2 = fun(Key, Val, Acc) -> handle_result(Key, Val, OutDir, Acc) end, 64 | indexer_misc:mapreduce(F1, F2, 0, Files). 65 | 66 | handle_result(Key, Vals, OutDir, Acc) -> 67 | add_to_file(OutDir, Key, Vals), 68 | Acc + 1. 69 | 70 | 71 | 72 | add_to_file(OutDir, Word, Is) -> 73 | L1 = map(fun(I) -> <> end, Is), 74 | OutFile = filename:join(OutDir, Word), 75 | case file:open(OutFile, [write,binary,raw,append]) of 76 | {ok, S} -> 77 | file:pwrite(S, 0, L1), 78 | file:close(S); 79 | {error, E} -> 80 | exit({ebadFileOp, OutFile, E}) 81 | end. 82 | 83 | 84 | sleep(T) -> 85 | receive 86 | after T -> true 87 | end. 88 | -------------------------------------------------------------------------------- /code/indexer-1.1/indexer_checkpoint.erl: -------------------------------------------------------------------------------- 1 | -module(indexer_checkpoint). 2 | 3 | -export([init/2, resume/1, checkpoint/2]). 4 | -import(filelib, [is_file/1]). 5 | 6 | %%% init(Dir, Term) -> true. %% define a checkpoitn dirextory and term X 7 | %% resume(Dir) -> {Check, X} %% Check is used in the *next* call to checkpoint 8 | %% %% X is a term 9 | %% checkpoint(Check, X) -> %% Set a new checkpoint 10 | %% Check' 11 | 12 | init(Dir, X) -> 13 | One = Dir ++ "/1.check", 14 | Two = Dir ++ "/2.check", 15 | case is_file(One) or is_file(Two) of 16 | true -> 17 | exit(eBadInit); 18 | false -> 19 | checkpoint({Dir, 1}, X), 20 | checkpoint({Dir, 2}, X) 21 | end. 22 | 23 | %% resume(Dir) -> {NextCheckPointFile, X} | error 24 | 25 | resume(Dir) -> 26 | R1 = recover(Dir ++ "/1.check"), 27 | R2 = recover(Dir ++ "/2.check"), 28 | case {R1, R2} of 29 | {error, error} -> error; 30 | {error, _} -> {{Dir,1}, element(2, R2)}; 31 | {_, error} -> {{Dir,2}, element(2, R1)}; 32 | {{T1,X},{T2,_}} when T1 > T2 -> {{Dir,2}, X}; 33 | {_,{_,X}} -> {{Dir,1}, X} 34 | end. 35 | 36 | recover(File) -> 37 | case file:read_file(File) of 38 | {ok, Bin} when size(Bin) > 32 -> 39 | {B1,B2} = split_binary(Bin, 16), 40 | case bin_to_md5(B2) of 41 | B1 -> 42 | binary_to_term(B2); 43 | _ -> 44 | error 45 | end; 46 | _ -> 47 | error 48 | end. 49 | 50 | checkpoint({Dir, Next}, X) -> 51 | File = Dir ++ "/" ++ integer_to_list(Next) ++ ".check", 52 | Time = now(), 53 | B = term_to_binary({Time, X}), 54 | CheckSum = bin_to_md5(B), 55 | ok = file:write_file(File, [CheckSum,B]), 56 | {Dir, 3-Next}. 57 | 58 | bin_to_md5(Bin) -> 59 | C1 = erlang:md5_init(), 60 | C2 = erlang:md5_update(C1, Bin), 61 | erlang:md5_final(C2). 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /code/indexer-1.1/indexer_dir_crawler.erl: -------------------------------------------------------------------------------- 1 | -module(indexer_dir_crawler). 2 | 3 | -export([start/1, next/1, test/0]). 4 | 5 | -import(lists, [foldl/3, suffix/2, sublist/3, map/2, filter/2, reverse/1]). 6 | -import(indexer_lib, [is_type/3]). 7 | 8 | %% start([Dir]) -> Cont 9 | %% next(Cont) -> {dir, Dir, Cont'} | done 10 | 11 | test() -> 12 | loop(next(start(["/home/ejoearm/erl/progs/doc/docbuilder"]))). 13 | 14 | loop({dir, Dir, Cont}) -> 15 | io:format("Dir=~p~n",[Dir]), 16 | loop(next(Cont)); 17 | loop(done) -> 18 | io:format("done:~n"). 19 | 20 | start(L) -> 21 | L. 22 | 23 | next([]) -> 24 | done; 25 | next([{Dir,[H1|T1]}|T2]) -> 26 | FullDirName = Dir++"/" ++ H1, 27 | next([FullDirName,{Dir,T1}|T2]); 28 | next([{_,[]}|T]) -> 29 | next(T); 30 | next([Dir|T]) -> 31 | case file:list_dir(Dir) of 32 | {ok, Things} -> 33 | Dirs = filter(fun(I) -> is_dir(I, Dir) end, Things), 34 | More = case Dirs of 35 | [] -> T; 36 | _ -> [{Dir,Dirs}|T] 37 | end, 38 | {dir, Dir,More}; 39 | {error, _} -> 40 | next(T) 41 | end. 42 | 43 | is_dir(I, Dir) -> 44 | filelib:is_dir(filename:join(Dir, I)). 45 | -------------------------------------------------------------------------------- /code/indexer-1.1/indexer_filenames_dets.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(indexer_filenames_dets). 3 | -export([open/1, close/0, test/0, filename2index/1, index2filename/1]). 4 | 5 | open(File) -> 6 | io:format("dets opened:~p~n", [File]), 7 | Bool = filelib:is_file(File), 8 | case dets:open_file(?MODULE, [{file, File}]) of 9 | {ok, ?MODULE} -> 10 | case Bool of 11 | true -> void; 12 | false -> ok = dets:insert(?MODULE, {free,1}) 13 | end, 14 | true; 15 | {error,_Reason} -> 16 | io:format("cannot open dets table~n"), 17 | exit(eDetsOpen) 18 | end. 19 | 20 | close() -> dets:close(?MODULE). 21 | 22 | 23 | 24 | filename2index(FileName) when is_binary(FileName) -> 25 | case dets:lookup(?MODULE, FileName) of 26 | [] -> 27 | [{_,Free}] = dets:lookup(?MODULE, free), 28 | ok = dets:insert(?MODULE, 29 | [{Free,FileName},{FileName,Free},{free,Free+1}]), 30 | Free; 31 | [{_,N}] -> 32 | N 33 | end. 34 | 35 | 36 | 37 | index2filename(Index) when is_integer(Index) -> 38 | case dets:lookup(?MODULE, Index) of 39 | [] -> error; 40 | [{_,Bin}] -> Bin 41 | end. 42 | 43 | 44 | 45 | test() -> 46 | file:delete("./filenames.dets"), 47 | open("./filenames.dets"), 48 | F = lib_files_find:files(".", "*.erl", true), 49 | lists:foreach(fun(I) -> filename2index(list_to_binary(I)) end, F). 50 | 51 | -------------------------------------------------------------------------------- /code/indexer-1.1/indexer_misc.erl: -------------------------------------------------------------------------------- 1 | -module(indexer_misc). 2 | -export([files_in_dir/1, foreach_word_in_file/3, foreach_word_in_string/3, 3 | mapreduce/4, search/3]). 4 | -import(lists, [filter/2, foreach/2, map/2, reverse/1]). 5 | 6 | 7 | %% evalute F(Word, Acc) -> Acc' for each word in the file File 8 | foreach_word_in_file(File, F, Acc) -> 9 | case file:read_file(File) of 10 | {ok, Bin} -> foreach_word_in_string(binary_to_list(Bin), F, Acc); 11 | _ -> void 12 | end. 13 | 14 | 15 | 16 | foreach_word_in_string(Str, F, Acc) -> 17 | case get_word(Str) of 18 | no -> 19 | Acc; 20 | {Word, Str1} -> 21 | Acc1 = F(Word, Acc), 22 | foreach_word_in_string(Str1, F, Acc1) 23 | end. 24 | 25 | 26 | isWordChar(X) when $A=< X, X=<$Z -> true; 27 | isWordChar(X) when $0=< X, X=<$9 -> true; 28 | isWordChar(X) when $a=< X, X=<$z -> true; 29 | isWordChar(_) -> false. 30 | 31 | get_word([H|T]) -> 32 | case isWordChar(H) of 33 | true -> collect_word(T, [H]); 34 | false -> get_word(T) 35 | end; 36 | get_word([]) -> 37 | no. 38 | 39 | collect_word([H|T]=All, L) -> 40 | case isWordChar(H) of 41 | true -> collect_word(T, [H|L]); 42 | false -> {reverse(L), All} 43 | end; 44 | collect_word([], L) -> 45 | {reverse(L), []}. 46 | 47 | %%----- mapreduce 48 | 49 | %% F1(Pid, X) -> sends {Key,Val} messages to Pid 50 | %% F2(Key, [Val], AccIn) -> AccOut 51 | 52 | mapreduce(F1, F2, Acc0, L) -> 53 | S = self(), 54 | Pid = spawn(fun() -> reduce(S, F1, F2, Acc0, L) end), 55 | receive 56 | {Pid, Result} -> 57 | Result 58 | end. 59 | 60 | reduce(Parent, F1, F2, Acc0, L) -> 61 | process_flag(trap_exit, true), 62 | ReducePid = self(), 63 | %% Create the Map processes 64 | %% One for each element X in L 65 | foreach(fun(X) -> 66 | spawn_link(fun() -> do_job(ReducePid, F1, X) end) 67 | end, L), 68 | N = length(L), 69 | %% make a dictionary to store the Keys 70 | Dict0 = dict:new(), 71 | %% Wait for N Map processes to terminate 72 | Dict1 = collect_replies(N, Dict0), 73 | Acc = dict:fold(F2, Acc0, Dict1), 74 | Parent ! {self(), Acc}. 75 | 76 | %% collect_replies(N, Dict) 77 | %% collect and merge {Key, Value} messages from N processes. 78 | %% When N processes have terminate return a dictionary 79 | %% of {Key, [Value]} pairs 80 | collect_replies(0, Dict) -> 81 | Dict; 82 | collect_replies(N, Dict) -> 83 | receive 84 | {Key, Val} -> 85 | case dict:is_key(Key, Dict) of 86 | true -> 87 | Dict1 = dict:append(Key, Val, Dict), 88 | collect_replies(N, Dict1); 89 | false -> 90 | Dict1 = dict:store(Key,[Val], Dict), 91 | collect_replies(N, Dict1) 92 | end; 93 | {'EXIT', _, _Why} -> 94 | collect_replies(N-1, Dict) 95 | end. 96 | 97 | %% Call F(Pid, X) 98 | %% F must send {Key, Value} messsages to Pid 99 | %% and then terminate 100 | 101 | do_job(ReducePid, F, X) -> 102 | F(ReducePid, X). 103 | 104 | files_in_dir(Dir) -> 105 | case file:list_dir(Dir) of 106 | {ok, Things} -> 107 | Things1 = map(fun(I) -> filename:join(Dir, I) end, Things), 108 | filter(fun filelib:is_regular/1, Things1); 109 | {error, _} -> 110 | [] 111 | end. 112 | 113 | %% @spec search(string(), string(), etsTable() -> 114 | %% none | tooMany | [filename::string()] 115 | 116 | search(Str, Dir, Ets) -> 117 | %% find the keywords using the same algorithm as in the indexing phase 118 | F1 = fun(Word, Acc) -> [Word|Acc] end, 119 | Words = indexer_misc:foreach_word_in_string(Str, F1, []), 120 | L1 = map(fun(I) -> indexer_words:process_word(I, Ets) end, Words), 121 | Words1 = [W || {yes, W} <- L1], 122 | Dir1 = filename:join(Dir, "index"), 123 | Indices = map(fun(I) -> read_indices(Dir1, I) end, Words1), 124 | %% Remove any empty lists and convert to sets 125 | Sets = [sets:from_list(X) || X <- Indices, X =/= []], 126 | case Sets of 127 | [] -> 128 | none; 129 | _ -> 130 | Unique = sets:intersection(Sets), 131 | Indices1 = sets:to_list(Unique), 132 | case length(Indices1) of 133 | N when N > 100 -> 134 | tooMany; 135 | _ -> 136 | map(fun indexer_filenames_dets:index2filename/1, Indices1) 137 | end 138 | end. 139 | 140 | read_indices(Dir, Word) -> 141 | Name = filename:join(Dir, Word), 142 | io:format("looking at:~p~n",[Name]), 143 | case file:read_file(Name) of 144 | {ok, Bin} -> 145 | bin2indices(Bin); 146 | _ -> 147 | [] 148 | end. 149 | 150 | bin2indices(<>) -> [I|bin2indices(B)]; 151 | bin2indices(<<>>) -> []. 152 | -------------------------------------------------------------------------------- /code/indexer-1.1/indexer_server.erl: -------------------------------------------------------------------------------- 1 | -module(indexer_server). 2 | 3 | -export([cold_start/2, 4 | start/1, 5 | filename2index/1, 6 | next_dir/0, 7 | ets_table/0, 8 | checkpoint/0, 9 | schedule_stop/0, 10 | search/1, 11 | should_i_stop/0, 12 | outdir/0, 13 | stop/0]). 14 | 15 | -export([init/1, handle_call/3, handle_cast/2, terminate/2]). 16 | -import(filename, [join/2]). 17 | 18 | %% indexer_server:cold_start(OutDir, SearchDirs). %% do once 19 | %% indexer_server:start(). %% start the server 20 | %% indexer_server:stop(). %% stop 21 | %% indexer_server:next_dir() -> Dir %% next dir to work at 22 | %% indexer_server:file2index(File) -> Index %% filename2index 23 | %% indexer_server:advance_dir() %% advance the dir pointer and set 24 | %% %% a new checkpoint 25 | %% indexer_server:outdir() %% The ouput directory 26 | %% indexer:search(Str) -> [File] %% search 27 | 28 | start(Dir) -> 29 | io:format("starting ~p ~p~n",[?MODULE, Dir]), 30 | gen_server:start({local,?MODULE}, ?MODULE, Dir, []). 31 | 32 | schedule_stop() -> 33 | gen_server:call(?MODULE, schedule_stop). 34 | 35 | should_i_stop() -> 36 | gen_server:call(?MODULE, should_i_stop). 37 | 38 | stop() -> 39 | gen_server:cast(?MODULE, stop). 40 | 41 | filename2index(File) -> gen_server:call(?MODULE, {f2i, File}). 42 | 43 | next_dir() -> gen_server:call(?MODULE, next_dir). 44 | checkpoint() -> gen_server:call(?MODULE, checkpoint). 45 | outdir() -> gen_server:call(?MODULE, outdir). 46 | ets_table() -> gen_server:call(?MODULE, ets_table). 47 | search(Str) -> gen_server:call(?MODULE, {search, Str}). 48 | 49 | %% Cold start is done ONCE *before* the server 50 | %% is started 51 | 52 | 53 | cold_start(OutputDir, SearchDirs) -> 54 | case file:list_dir(OutputDir) of 55 | {ok, []} -> 56 | OutputDir1 = join(OutputDir,"index"), 57 | io:format("Creating ~p~n",[OutputDir1]), 58 | file:make_dir(OutputDir1), 59 | Cont = indexer_dir_crawler:start(SearchDirs), 60 | Check = {OutputDir, Cont}, 61 | io:format("creating checkpoint:~p~n",[Check]), 62 | indexer_checkpoint:init(OutputDir, Check); 63 | _ -> 64 | exit({eDirNotEmptyOrMissing, OutputDir}) 65 | end. 66 | 67 | 68 | -record(env,{ets, cont, nextCP, outdir, stop=false}). 69 | 70 | init(Dir) -> 71 | io:format("restarting:~p~n", [Dir]), 72 | {Next, {OutDir, Cont}} = indexer_checkpoint:resume(Dir), 73 | io:format("resume with:~p ~p~n",[Next, Cont]), 74 | indexer_filenames_dets:open(join(Dir,"filenames.dets")), 75 | io:format("opening trigrams ~n"), 76 | Tab = indexer_trigrams:open(), 77 | {ok, #env{ets = Tab, outdir=OutDir, cont=Cont, nextCP = Next}}. 78 | 79 | handle_call({f2i, File}, _From, S) -> 80 | B = list_to_binary(File), 81 | {reply, indexer_filenames_dets:filename2index(B), S}; 82 | handle_call(ets_table, _From, S) -> 83 | {reply, S#env.ets, S}; 84 | handle_call(next_dir, _From, S) -> 85 | Cont = S#env.cont, 86 | case indexer_dir_crawler:next(Cont) of 87 | {dir, Dir, _} -> 88 | {reply, {ok, Dir}, S}; 89 | done -> 90 | {reply, done, S} 91 | end; 92 | handle_call(checkpoint, _From, S) -> 93 | Cont = S#env.cont, 94 | case indexer_dir_crawler:next(Cont) of 95 | {dir, _Dir, Cont1} -> 96 | Next = S#env.nextCP, 97 | OutDir = S#env.outdir, 98 | Next1 = indexer_checkpoint:checkpoint(Next, {OutDir, Cont1}), 99 | S1 = S#env{nextCP = Next1, cont=Cont1}, 100 | {reply, ok, S1}; 101 | done -> 102 | {reply, done, S} 103 | end; 104 | handle_call(schedule_stop, _From, S) -> 105 | {reply, ack, S#env{stop=true}}; 106 | handle_call({search, Str}, _From,S) -> 107 | Result = indexer_misc:search(Str, S#env.outdir, S#env.ets), 108 | {reply, Result, S}; 109 | handle_call(should_i_stop, _From, S) -> 110 | {reply, S#env.stop, S}; 111 | handle_call(outdir, _From, S) -> 112 | {reply, S#env.outdir, S}. 113 | 114 | handle_cast(stop, S) -> 115 | {stop, normal, S}. 116 | 117 | terminate(Reason, S) -> 118 | Ets = S#env.ets, 119 | indexer_trigrams:close(Ets), 120 | indexer_filenames_dets:close(), 121 | io:format("stopping ~p~n",[Reason]). 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /code/indexer-1.1/indexer_trigrams.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(indexer_trigrams). 3 | -export([for_each_trigram_in_the_english_language/2, 4 | make_tables/0, timer_tests/0, 5 | open/0, close/1, is_word/2, 6 | howManyTrigrams/0, 7 | make_dict/0, make_ordered_set/0, makeSet/0, 8 | lookup_all_ets/2, lookup_all_dict/2 9 | ]). 10 | -import(lists, [reverse/1]). 11 | 12 | 13 | 14 | make_tables() -> 15 | io:format("Building trigrams -- make take some time~n"), 16 | makeSet(). 17 | 18 | 19 | 20 | make_ordered_set() -> makeAset(ordered_set, "trigramsOS.tab"). 21 | makeSet() -> makeAset(set, "trigramsS.tab"). 22 | 23 | makeAset(Type, FileName) -> 24 | Tab = ets:new(table, [Type]), 25 | F = fun(Str, _) -> ets:insert(Tab, {list_to_binary(Str)}) end, 26 | for_each_trigram_in_the_english_language(F, 0), 27 | ets:tab2file(Tab, FileName), 28 | Size = ets:info(Tab, size), 29 | ets:delete(Tab), 30 | Size. 31 | 32 | 33 | 34 | make_dict() -> 35 | D = dict:new(), 36 | F = fun(Str, Dict) -> dict:store(list_to_binary(Str),[],Dict) end, 37 | D1 = for_each_trigram_in_the_english_language(F, D), 38 | file:write_file("trigrams.dict", [term_to_binary(D1)]). 39 | 40 | 41 | 42 | timer_tests() -> 43 | time_lookup_set("Ordered Set", "trigramsOS.tab"), 44 | time_lookup_set("Set", "trigramsS.tab"), 45 | time_lookup_dict(). 46 | 47 | time_lookup_set(Type, File) -> 48 | {ok, Tab} = ets:file2tab(File), 49 | L = ets:tab2list(Tab), 50 | Size = length(L), 51 | {M, _} = timer:tc(?MODULE, lookup_all_ets, [Tab, L]), 52 | io:format("~s lookup=~p micro seconds~n",[Type, M/Size]), 53 | ets:delete(Tab). 54 | 55 | lookup_all_ets(Tab, L) -> 56 | lists:foreach(fun({K}) -> ets:lookup(Tab, K) end, L). 57 | 58 | time_lookup_dict() -> 59 | {ok, Bin} = file:read_file("trigrams.dict"), 60 | Dict = binary_to_term(Bin), 61 | Keys = [Key || {Key,_} <- dict:to_list(Dict)], 62 | Size = length(Keys), 63 | {M, _} = timer:tc(?MODULE, lookup_all_dict, [Dict, Keys]), 64 | io:format("Dict lookup=~p micro seconds~n",[M/Size]). 65 | 66 | lookup_all_dict(Dict, L) -> 67 | lists:foreach(fun(Key) -> dict:find(Key, Dict) end, L). 68 | 69 | 70 | howManyTrigrams() -> 71 | F = fun(_, N) -> 1 + N end, 72 | for_each_trigram_in_the_english_language(F, 0). 73 | 74 | %% An iterator that iterates through all trigrams in the language 75 | 76 | for_each_trigram_in_the_english_language(F, A0) -> 77 | {ok, Bin0} = file:read_file("../354984si.ngl.gz"), 78 | Bin = zlib:gunzip(Bin0), 79 | scan_word_list(binary_to_list(Bin), F, A0). 80 | 81 | scan_word_list([], _, A) -> 82 | A; 83 | scan_word_list(L, F, A) -> 84 | {Word, L1} = get_next_word(L, []), 85 | A1 = scan_trigrams([$\s|Word], F, A), 86 | scan_word_list(L1, F, A1). 87 | 88 | %% scan the word looking for \r\n 89 | %% the second argument is the word (reversed) so it 90 | %% has to be reversed when we find \r\n or run out of characters 91 | 92 | get_next_word([$\r,$\n|T], L) -> {reverse([$\s|L]), T}; 93 | get_next_word([H|T], L) -> get_next_word(T, [H|L]); 94 | get_next_word([], L) -> {reverse([$\s|L]), []}. 95 | 96 | scan_trigrams([X,Y,Z], F, A) -> 97 | F([X,Y,Z], A); 98 | scan_trigrams([X,Y,Z|T], F, A) -> 99 | A1 = F([X,Y,Z], A), 100 | scan_trigrams([Y,Z|T], F, A1); 101 | scan_trigrams(_, _, A) -> 102 | A. 103 | 104 | 105 | %% access routines 106 | %% open() -> Table 107 | %% close(Table) 108 | %% is_word(Table, String) -> Bool 109 | 110 | 111 | is_word(Tab, Str) -> is_word1(Tab, "\s" ++ Str ++ "\s"). 112 | 113 | is_word1(Tab, [_,_,_]=X) -> is_this_a_trigram(Tab, X); 114 | is_word1(Tab, [A,B,C|D]) -> 115 | case is_this_a_trigram(Tab, [A,B,C]) of 116 | true -> is_word1(Tab, [B,C|D]); 117 | false -> false 118 | end; 119 | is_word1(_, _) -> 120 | false. 121 | 122 | is_this_a_trigram(Tab, X) -> 123 | case ets:lookup(Tab, list_to_binary(X)) of 124 | [] -> false; 125 | _ -> true 126 | end. 127 | 128 | open() -> 129 | {ok, I} = ets:file2tab(filename:dirname(code:which(?MODULE)) 130 | ++ "/trigramsS.tab"), 131 | I. 132 | 133 | close(Tab) -> ets:delete(Tab). 134 | 135 | -------------------------------------------------------------------------------- /code/lib_auth_cs.erl: -------------------------------------------------------------------------------- 1 | -module(lib_auth_cs). 2 | 3 | %% authenticated client/server 4 | 5 | -compile(export_all). 6 | 7 | 8 | start_server(Port, MFA, Secret, Trace, Max) -> 9 | lib_tcp_mm:start_server(Port, 10 | {?MODULE, start_auth_server, {MFA, Secret}}, 11 | Trace, 12 | Max). 13 | 14 | 15 | start_auth_client(Host, Port, MFA, Secret, Trace) -> 16 | lib_tcp_mm:start_client(Host, Port, 17 | {?MODULE, start_auth_client, {MFA, Secret}}, 18 | Trace). 19 | 20 | 21 | %%______________________________________________________________________ 22 | %% 23 | 24 | start_auth_server(C, {{Mod,Func,ArgsS}, Secret}) -> 25 | case Secret of 26 | none -> 27 | %% no authentication necessary 28 | C ! {msg, auth}, 29 | apply(Mod, Func, [C, ArgsS]); 30 | _ -> 31 | Ran =lib_primes:make_random_int(20), 32 | C ! {msg, {challenge, Ran}}, 33 | receive 34 | {msg, {response, R}} -> 35 | Expect = md5:string(Ran ++ Secret), 36 | case R of 37 | Expect -> 38 | C ! {msg, auth}, 39 | apply(Mod, Func, [C, ArgsS]); 40 | _ -> 41 | C ! {msg, eBadResponse}, 42 | exit(protocol) 43 | end; 44 | _ -> 45 | exit(protocol) 46 | end 47 | end. 48 | 49 | start_auth_client(C, {{Mod, Func, ArgsC}, Secret}) -> 50 | io:format("lib_auth_cs start auth client:~p~n", 51 | [{{Mod, Func, ArgsC}, Secret}]), 52 | receive 53 | {msg, auth} -> 54 | apply(Mod, Func, [C, ArgsC]); 55 | {msg, {challenge, Challenge}} -> 56 | case Secret of 57 | none -> 58 | exit(eNoSecret); 59 | _ -> 60 | R = md5:string(Challenge ++ Secret), 61 | C ! {msg, {response, R}}, 62 | receive 63 | {msg, auth} -> 64 | apply(Mod, Func, [C, ArgsC]); 65 | _ -> 66 | exit(protocol) 67 | end 68 | end; 69 | _ -> 70 | exit(protocol) 71 | end. 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /code/lib_filenames_dets.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(lib_filenames_dets). 3 | -export([open/1, close/0, test/0, filename2index/1, index2filename/1]). 4 | 5 | open(File) -> 6 | io:format("dets opened:~p~n", [File]), 7 | Bool = filelib:is_file(File), 8 | case dets:open_file(?MODULE, [{file, File}]) of 9 | {ok, ?MODULE} -> 10 | case Bool of 11 | true -> void; 12 | false -> ok = dets:insert(?MODULE, {free,1}) 13 | end, 14 | true; 15 | {error,_Reason} -> 16 | io:format("cannot open dets table~n"), 17 | exit(eDetsOpen) 18 | end. 19 | 20 | close() -> dets:close(?MODULE). 21 | 22 | 23 | 24 | filename2index(FileName) when is_binary(FileName) -> 25 | case dets:lookup(?MODULE, FileName) of 26 | [] -> 27 | [{_,Free}] = dets:lookup(?MODULE, free), 28 | ok = dets:insert(?MODULE, 29 | [{Free,FileName},{FileName,Free},{free,Free+1}]), 30 | Free; 31 | [{_,N}] -> 32 | N 33 | end. 34 | 35 | 36 | 37 | index2filename(Index) when is_integer(Index) -> 38 | case dets:lookup(?MODULE, Index) of 39 | [] -> error; 40 | [{_,Bin}] -> Bin 41 | end. 42 | 43 | 44 | 45 | test() -> 46 | file:delete("./filenames.dets"), 47 | open("./filenames.dets"), 48 | F = lib_files_find:files(".", "*.erl", true), 49 | lists:foreach(fun(I) -> filename2index(list_to_binary(I)) end, F). 50 | 51 | -------------------------------------------------------------------------------- /code/lib_files_find.erl: -------------------------------------------------------------------------------- 1 | -module(lib_files_find). 2 | -export([files/3, files/5]). 3 | 4 | -include_lib("kernel/include/file.hrl"). 5 | 6 | files(Dir, Re, Flag) -> 7 | Re1 = regexp:sh_to_awk(Re), 8 | lists:reverse(files(Dir, Re1, Flag, fun(File, Acc) ->[File|Acc] end, [])). 9 | 10 | files(Dir, Reg, Recursive, Fun, Acc) -> 11 | case file:list_dir(Dir) of 12 | {ok, Files} -> find_files(Files, Dir, Reg, Recursive, Fun, Acc); 13 | {error, _} -> Acc 14 | end. 15 | 16 | find_files([File|T], Dir, Reg, Recursive, Fun, Acc0) -> 17 | FullName = Dir ++ [$/|File], 18 | case file_type(FullName) of 19 | regular -> 20 | case regexp:match(FullName, Reg) of 21 | {match, _, _} -> 22 | Acc = Fun(FullName, Acc0), 23 | find_files(T, Dir, Reg, Recursive, Fun, Acc); 24 | _ -> 25 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 26 | end; 27 | directory -> 28 | case Recursive of 29 | true -> 30 | Acc1 = files(FullName, Reg, Recursive, Fun, Acc0), 31 | find_files(T, Dir, Reg, Recursive, Fun, Acc1); 32 | false -> 33 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 34 | end; 35 | error -> 36 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 37 | end; 38 | find_files([], _, _, _, _, A) -> 39 | A. 40 | 41 | file_type(File) -> 42 | case file:read_file_info(File) of 43 | {ok, Facts} -> 44 | case Facts#file_info.type of 45 | regular -> regular; 46 | directory -> directory; 47 | _ -> error 48 | end; 49 | _ -> 50 | error 51 | end. 52 | 53 | -------------------------------------------------------------------------------- /code/lib_find.erl: -------------------------------------------------------------------------------- 1 | -module(lib_find). 2 | -export([files/3, files/5]). 3 | -import(lists, [reverse/1]). 4 | 5 | -include_lib("kernel/include/file.hrl"). 6 | 7 | files(Dir, Re, Flag) -> 8 | Re1 = regexp:sh_to_awk(Re), 9 | reverse(files(Dir, Re1, Flag, fun(File, Acc) ->[File|Acc] end, [])). 10 | 11 | files(Dir, Reg, Recursive, Fun, Acc) -> 12 | case file:list_dir(Dir) of 13 | {ok, Files} -> find_files(Files, Dir, Reg, Recursive, Fun, Acc); 14 | {error, _} -> Acc 15 | end. 16 | 17 | find_files([File|T], Dir, Reg, Recursive, Fun, Acc0) -> 18 | FullName = filename:join([Dir,File]), 19 | case file_type(FullName) of 20 | regular -> 21 | case regexp:match(FullName, Reg) of 22 | {match, _, _} -> 23 | Acc = Fun(FullName, Acc0), 24 | find_files(T, Dir, Reg, Recursive, Fun, Acc); 25 | _ -> 26 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 27 | end; 28 | directory -> 29 | case Recursive of 30 | true -> 31 | Acc1 = files(FullName, Reg, Recursive, Fun, Acc0), 32 | find_files(T, Dir, Reg, Recursive, Fun, Acc1); 33 | false -> 34 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 35 | end; 36 | error -> 37 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 38 | end; 39 | find_files([], _, _, _, _, A) -> 40 | A. 41 | 42 | file_type(File) -> 43 | case file:read_file_info(File) of 44 | {ok, Facts} -> 45 | case Facts#file_info.type of 46 | regular -> regular; 47 | directory -> directory; 48 | _ -> error 49 | end; 50 | _ -> 51 | error 52 | end. 53 | 54 | -------------------------------------------------------------------------------- /code/lib_io_widget.erl: -------------------------------------------------------------------------------- 1 | -module(lib_io_widget). 2 | 3 | -export([start/1, test/0, 4 | set_handler/2, set_prompt/2, set_title/2, insert_str/2]). 5 | 6 | start(Pid) -> 7 | gs:start(), 8 | spawn_link(fun() -> widget(Pid) end). 9 | 10 | set_title(Pid, Str) -> Pid ! {title, Str}. 11 | set_handler(Pid, Fun) -> Pid ! {handler, Fun}. 12 | set_prompt(Pid, Str) -> Pid ! {prompt, Str}. 13 | insert_str(Pid, Str) -> Pid ! {insert, Str}. 14 | 15 | widget(Pid) -> 16 | Size = [{width,500},{height,200}], 17 | Win = gs:window(gs:start(), 18 | [{map,true},{configure,true},{title,"window"}|Size]), 19 | gs:frame(packer, Win,[{packer_x, [{stretch,1,500}]}, 20 | {packer_y, [{stretch,10,120,100}, 21 | {stretch,1,15,15}]}]), 22 | gs:create(editor,editor,packer, [{pack_x,1},{pack_y,1},{vscroll,right}]), 23 | gs:create(entry, entry, packer, [{pack_x,1},{pack_y,2},{keypress,true}]), 24 | gs:config(packer, Size), 25 | Prompt = " > ", 26 | gs:config(entry, {insert,{0,Prompt}}), 27 | loop(Win, Pid, Prompt, fun parse/1). 28 | 29 | 30 | loop(Win, Pid, Prompt, Parse) -> 31 | receive 32 | {handler, Fun} -> 33 | loop(Win, Pid, Prompt, Fun); 34 | {prompt, Str} -> 35 | %% this clobbers the line being input ... 36 | %% this could be fixed - hint 37 | gs:config(entry, {delete,{0,last}}), 38 | gs:config(entry, {insert,{0,Str}}), 39 | loop(Win, Pid, Str, Parse); 40 | {title, Str} -> 41 | gs:config(Win, [{title, Str}]), 42 | loop(Win, Pid, Prompt, Parse); 43 | {insert, Str} -> 44 | gs:config(editor, {insert,{'end',Str}}), 45 | scroll_to_show_last_line(), 46 | loop(Win, Pid, Prompt, Parse); 47 | {gs,_,destroy,_,_} -> 48 | io:format("Destroyed~n",[]), 49 | Pid ! {self(), closed}; 50 | {gs, entry,keypress,_,['Return'|_]} -> 51 | Text = gs:read(entry, text), 52 | io:format("Read:~p~n",[Text]), 53 | gs:config(entry, {delete,{0,last}}), 54 | gs:config(entry, {insert,{0,Prompt}}), 55 | case (catch Parse(Text)) of 56 | {'EXIT', _Error} -> 57 | self() ! {insert, "** bad input**\n** /h for help\n"}; 58 | Term -> 59 | Pid ! {self(), Term} 60 | end, 61 | loop(Win, Pid, Prompt, Parse); 62 | {gs,_,configure,[],[W,H,_,_]} -> 63 | gs:config(packer, [{width,W},{height,H}]), 64 | loop(Win, Pid, Prompt, Parse); 65 | {gs, entry,keypress,_,_} -> 66 | loop(Win, Pid, Prompt, Parse); 67 | Any -> 68 | io:format("Discarded:~p~n",[Any]), 69 | loop(Win, Pid, Prompt, Parse) 70 | end. 71 | 72 | scroll_to_show_last_line() -> 73 | Size = gs:read(editor, size), 74 | Height = gs:read(editor, height), 75 | CharHeight = gs:read(editor, char_height), 76 | TopRow = Size - Height/CharHeight, 77 | if TopRow > 0 -> gs:config(editor, {vscrollpos, TopRow}); 78 | true -> gs:config(editor, {vscrollpos, 0}) 79 | end. 80 | 81 | test() -> 82 | spawn(fun() -> test1() end). 83 | 84 | test1() -> 85 | W = io_widget:start(self()), 86 | io_widget:set_title(W, "Test window"), 87 | loop(W). 88 | 89 | loop(W) -> 90 | receive 91 | {W, {str, Str}} -> 92 | Str1 = Str ++ "\n", 93 | io_widget:insert_str(W, Str1), 94 | loop(W) 95 | end. 96 | 97 | parse(Str) -> 98 | {str, Str}. 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /code/lib_lin.erl: -------------------------------------------------------------------------------- 1 | -module(lib_lin). 2 | 3 | %% (c) Joe Armstrong 1998 4 | 5 | -export([pow/3, inv/2, solve/2, str2int/1, int2str/1, gcd/2]). 6 | 7 | %% pow(A, B, M) => (A^B) mod M 8 | %% examples pow(9726,3533,11413) = 5761 9 | %% pow(5971,6597,11413) = 9726 10 | 11 | 12 | pow(A, 1, M) -> 13 | A rem M; 14 | pow(A, 2, M) -> 15 | A*A rem M; 16 | pow(A, B, M) -> 17 | B1 = B div 2, 18 | B2 = B - B1, 19 | %% B2 = B1 or B1+1 20 | P = pow(A, B1, M), 21 | case B2 of 22 | B1 -> (P*P) rem M; 23 | _ -> (P*P*A) rem M 24 | end. 25 | 26 | 27 | %% inv(A, B) = C | no_inverse 28 | %% computes C such that 29 | %% A*C mod B = 1 30 | %% computes A^-1 mod B 31 | %% examples inv(28, 75) = 67. 32 | %% inv(3533, 11200) = 6597 33 | %% inv(6597, 11200) = 3533 34 | 35 | 36 | inv(A, B) -> 37 | case solve(A, B) of 38 | {X, _} -> 39 | if X < 0 -> X + B; 40 | true -> X 41 | end; 42 | _ -> 43 | no_inverse 44 | end. 45 | 46 | 47 | %% solve(A, B) => {X, Y} | insoluble 48 | %% solve the linear congruence 49 | %% A * X - B * Y = 1 50 | 51 | 52 | solve(A, B) -> 53 | case catch s(A,B) of 54 | insoluble -> insoluble; 55 | {X, Y} -> 56 | case A * X - B * Y of 57 | 1 -> {X, Y}; 58 | _Other -> error 59 | end 60 | end. 61 | 62 | s(_, 0) -> throw(insoluble); 63 | s(_, 1) -> {0, -1}; 64 | s(_, -1) -> {0, 1}; 65 | s(A, B) -> 66 | K1 = A div B, 67 | K2 = A - K1*B, 68 | {Tmp, X} = s(B, -K2), 69 | {X, K1 * X - Tmp}. 70 | 71 | 72 | 73 | 74 | %% converts a string to a base 256 integer 75 | %% converts a base 256 integer to a string 76 | 77 | 78 | str2int(Str) -> str2int(Str, 0). 79 | 80 | str2int([H|T], N) -> str2int(T, N*256+H); 81 | str2int([], N) -> N. 82 | 83 | int2str(N) -> int2str(N, []). 84 | 85 | int2str(N, L) when N =< 0 -> L; 86 | int2str(N, L) -> 87 | N1 = N div 256, 88 | H = N - N1 * 256, 89 | int2str(N1, [H|L]). 90 | 91 | 92 | %% greatest common devisor 93 | 94 | 95 | gcd(A, B) when A < B -> gcd(B, A); 96 | gcd(A, 0) -> A; 97 | gcd(A, B) -> gcd(B, A rem B). 98 | 99 | -------------------------------------------------------------------------------- /code/lib_md5.erl: -------------------------------------------------------------------------------- 1 | -module(lib_md5). 2 | 3 | %% modfied to use BIFs 4 | 5 | -export([string/1, file/1, bin/1, binAsBin/1, digest2str/1]). 6 | 7 | -define(BLOCKSIZE, 32768). 8 | -define(IN(X,Min,Max), X >= Min, X =< Max). 9 | 10 | %% md5:string(string()) -> BinDigest 11 | %% md5:file(FileName) -> {ok, BinDigest} | {error, E} 12 | %% md5:digest2str(BinDigest) -> StringDigest 13 | 14 | %% md5:file works with chunks so should work correctly with extremely 15 | %% large files 16 | 17 | string(Str) -> digest2str(erlang:md5(Str)). 18 | 19 | file(File) -> 20 | case file:open(File, [binary,raw,read]) of 21 | {ok, P} -> loop(P, erlang:md5_init()); 22 | Error -> Error 23 | end. 24 | 25 | loop(P, C) -> 26 | case file:read(P, ?BLOCKSIZE) of 27 | {ok, Bin} -> 28 | loop(P, erlang:md5_update(C, Bin)); 29 | eof -> 30 | file:close(P), 31 | {ok, erlang:md5_final(C)} 32 | end. 33 | 34 | digest2str(Digest) -> bin2str(binary_to_list(Digest)). 35 | 36 | bin2str([H|T]) -> 37 | {H1, H2} = byte2hex(H), 38 | [H1,H2|bin2str(T)]; 39 | bin2str([]) -> 40 | []. 41 | 42 | byte2hex(X) -> 43 | {nibble2hex(X bsr 4), nibble2hex(X band 15)}. 44 | 45 | nibble2hex(X) when ?IN(X, 0, 9) -> X + $0; 46 | nibble2hex(X) when ?IN(X, 10, 15) -> X - 10 + $a. 47 | 48 | %% compute the md5 checksum of a binary 49 | bin(Bin) -> 50 | C1 = erlang:md5_init(), 51 | C2 = erlang:md5_update(C1, Bin), 52 | C3 = erlang:md5_final(C2), 53 | digest2str(C3). 54 | 55 | binAsBin(Bin) -> 56 | C1 = erlang:md5_init(), 57 | C2 = erlang:md5_update(C1, Bin), 58 | erlang:md5_final(C2). 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /code/lib_primes.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(lib_primes). 3 | 4 | -export([make_prime/1, is_prime/1, make_random_int/1]). 5 | 6 | %% Make a prime with at least K decimal digits. Here 7 | %% we use 'Bertrand's postulate. Bertrand's postulate 8 | %% is that for every N > 3, there is a prime P 9 | %% satisfying N < P < 2N - 2. This was proved by 10 | %% Tchebychef in 1850. (Erdos improved this proof in 1932) 11 | 12 | make_prime(1) -> 13 | lists:nth(random:uniform(4), [2,3,5,7]); 14 | make_prime(K) when K > 0 -> 15 | new_seed(), 16 | N = make_random_int(K), 17 | if N > 3 -> 18 | io:format("Generating a ~w digit prime ",[K]), 19 | MaxTries = N - 3, 20 | P1 = make_prime(MaxTries, N+1), 21 | io:format("~n",[]), 22 | P1; 23 | true -> 24 | make_prime(K) 25 | end. 26 | 27 | make_prime(0, _) -> 28 | exit(impossible); 29 | make_prime(K, P) -> 30 | io:format(".",[]), 31 | case is_prime(P) of 32 | true -> P; 33 | false -> make_prime(K-1, P+1) 34 | end. 35 | 36 | %% Fermat's little theorem says that if 37 | %% N is a prime and if A < N then 38 | %% A^N mod N = A 39 | 40 | is_prime(D) when D < 10 -> 41 | lists:member(D, [2,3,5,7]); 42 | is_prime(D) -> 43 | new_seed(), 44 | is_prime(D, 100). 45 | 46 | is_prime(D, Ntests) -> 47 | N = length(integer_to_list(D)) -1, 48 | is_prime(Ntests, D, N). 49 | 50 | is_prime(0, _, _) -> true; 51 | is_prime(Ntest, N, Len) -> 52 | K = random:uniform(Len), 53 | %% A is a random number less than N 54 | A = make_random_int(K), 55 | if 56 | A < N -> 57 | case lib_lin:pow(A,N,N) of 58 | A -> is_prime(Ntest-1,N,Len); 59 | _ -> false 60 | end; 61 | true -> 62 | is_prime(Ntest, N, Len) 63 | end. 64 | 65 | 66 | %% make_random_int(N) -> a random integer with N digits. 67 | 68 | 69 | make_random_int(N) -> new_seed(), make_random_int(N, 0). 70 | 71 | make_random_int(0, D) -> D; 72 | make_random_int(N, D) -> 73 | make_random_int(N-1, D*10 + (random:uniform(10)-1)). 74 | %END:make_ran_int 75 | 76 | 77 | new_seed() -> 78 | {_,_,X} = erlang:now(), 79 | {H,M,S} = time(), 80 | H1 = H * X rem 32767, 81 | M1 = M * X rem 32767, 82 | S1 = S * X rem 32767, 83 | put(random_seed, {H1,M1,S1}). 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /code/lib_rsa.erl: -------------------------------------------------------------------------------- 1 | -module(lib_rsa). 2 | 3 | -export([make_sig/1, make_sig/2]). 4 | 5 | make_sig(Who, Len) when Len > 79 -> 6 | {Public, Private} = make_sig(Len), 7 | file:write_file(Who ++ ".pub", term_to_binary(Public)), 8 | file:write_file(Who ++ ".pri", term_to_binary(Private)), 9 | {keyfilecreated,for,Who}. 10 | 11 | %% The "book" says ... 12 | %% 1. Bob generates two primes p and q 13 | %% 2. Bob computes n = pq and phi(n) = (p-1)*(q-1) 14 | %% 3. Bob chooses a random b(0 < b < phi(n)) such that 15 | %% gcd(b, phi(n)) = 1 16 | %% 4. Bob computes a = b^(-1) mod phi(n) using the Euclidean algorithm 17 | %% 5. Bob publishes n and b in a directory as his public key. 18 | 19 | %START:make_sig 20 | make_sig(Len) -> 21 | %% generate two digit prime numbers 22 | P = primes:make_prime(Len), 23 | io:format("P = ~p~n", [P]), 24 | Q = primes:make_prime(Len), 25 | io:format("Q = ~p~n", [Q]), 26 | N = P*Q, 27 | io:format("N = ~p~n", [N]), 28 | Phi = (P-1)*(Q-1), 29 | %% now make B such that B < Phi and gcd(B, Phi) = 1 30 | B = b(Phi), 31 | io:format("Public key (B) = ~p~n", [B]), 32 | A = lin:inv(B, Phi), 33 | io:format("Private key (A) = ~p~n", [A]), 34 | {{B,N},{A,N}}. 35 | 36 | b(Phi) -> 37 | io:format("Generating a public key B "), 38 | K = length(integer_to_list(Phi)) - 1, 39 | B = b(1, K, Phi), 40 | io:format("~n", []), 41 | B. 42 | 43 | b(Try, K, Phi) -> 44 | io:format("."), 45 | B = primes:make(K), 46 | if 47 | B < Phi -> 48 | case lin:gcd(B, Phi) of 49 | 1 -> B; 50 | _ -> b(Try+1, K, Phi) 51 | end; 52 | true -> 53 | b(Try, K, Phi) 54 | end. 55 | %END:make_sig 56 | 57 | 58 | -------------------------------------------------------------------------------- /code/lib_trigrams.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(lib_trigrams). 3 | -export([for_each_trigram_in_the_english_language/2, 4 | make_tables/0, timer_tests/0, 5 | open/0, close/1, is_word/2, 6 | how_many_trigrams/0, 7 | make_ets_set/0, make_ets_ordered_set/0, make_mod_set/0, 8 | lookup_all_ets/2, lookup_all_set/2 9 | ]). 10 | -import(lists, [reverse/1]). 11 | 12 | 13 | 14 | make_tables() -> 15 | {Micro1, N} = timer:tc(?MODULE, how_many_trigrams, []), 16 | io:format("Counting - No of trigrams=~p time/trigram=~p~n",[N,Micro1/N]), 17 | {Micro2, Ntri} = timer:tc(?MODULE, make_ets_ordered_set, []), 18 | FileSize1 = filelib:file_size("trigramsOS.tab"), 19 | io:format("Ets ordered Set size=~p time/trigram=~p~n",[FileSize1/Ntri, 20 | Micro2/N]), 21 | {Micro3, _} = timer:tc(?MODULE, make_ets_set, []), 22 | FileSize2 = filelib:file_size("trigramsS.tab"), 23 | io:format("Ets set size=~p time/trigram=~p~n",[FileSize2/Ntri, Micro3/N]), 24 | {Micro4, _} = timer:tc(?MODULE, make_mod_set, []), 25 | FileSize3 = filelib:file_size("trigrams.set"), 26 | io:format("Module sets size=~p time/trigram=~p~n",[FileSize3/Ntri, Micro4/N]). 27 | 28 | 29 | 30 | make_ets_ordered_set() -> make_a_set(ordered_set, "trigramsOS.tab"). 31 | make_ets_set() -> make_a_set(set, "trigramsS.tab"). 32 | 33 | make_a_set(Type, FileName) -> 34 | Tab = ets:new(table, [Type]), 35 | F = fun(Str, _) -> ets:insert(Tab, {list_to_binary(Str)}) end, 36 | for_each_trigram_in_the_english_language(F, 0), 37 | ets:tab2file(Tab, FileName), 38 | Size = ets:info(Tab, size), 39 | ets:delete(Tab), 40 | Size. 41 | 42 | 43 | 44 | make_mod_set() -> 45 | D = sets:new(), 46 | F = fun(Str, Set) -> sets:add_element(list_to_binary(Str),Set) end, 47 | D1 = for_each_trigram_in_the_english_language(F, D), 48 | file:write_file("trigrams.set", [term_to_binary(D1)]). 49 | 50 | 51 | 52 | timer_tests() -> 53 | time_lookup_ets_set("Ets ordered Set", "trigramsOS.tab"), 54 | time_lookup_ets_set("Ets set", "trigramsS.tab"), 55 | time_lookup_module_sets(). 56 | 57 | time_lookup_ets_set(Type, File) -> 58 | {ok, Tab} = ets:file2tab(File), 59 | L = ets:tab2list(Tab), 60 | Size = length(L), 61 | {M, _} = timer:tc(?MODULE, lookup_all_ets, [Tab, L]), 62 | io:format("~s lookup=~p micro seconds~n",[Type, M/Size]), 63 | ets:delete(Tab). 64 | 65 | lookup_all_ets(Tab, L) -> 66 | lists:foreach(fun({K}) -> ets:lookup(Tab, K) end, L). 67 | 68 | time_lookup_module_sets() -> 69 | {ok, Bin} = file:read_file("trigrams.set"), 70 | Set = binary_to_term(Bin), 71 | Keys = sets:to_list(Set), 72 | Size = length(Keys), 73 | {M, _} = timer:tc(?MODULE, lookup_all_set, [Set, Keys]), 74 | io:format("Module set lookup=~p micro seconds~n",[M/Size]). 75 | 76 | lookup_all_set(Set, L) -> 77 | lists:foreach(fun(Key) -> sets:is_element(Key, Set) end, L). 78 | 79 | 80 | how_many_trigrams() -> 81 | F = fun(_, N) -> 1 + N end, 82 | for_each_trigram_in_the_english_language(F, 0). 83 | 84 | %% An iterator that iterates through all trigrams in the language 85 | 86 | for_each_trigram_in_the_english_language(F, A0) -> 87 | {ok, Bin0} = file:read_file("354984si.ngl.gz"), 88 | Bin = zlib:gunzip(Bin0), 89 | scan_word_list(binary_to_list(Bin), F, A0). 90 | 91 | scan_word_list([], _, A) -> 92 | A; 93 | scan_word_list(L, F, A) -> 94 | {Word, L1} = get_next_word(L, []), 95 | A1 = scan_trigrams([$\s|Word], F, A), 96 | scan_word_list(L1, F, A1). 97 | 98 | %% scan the word looking for \r\n 99 | %% the second argument is the word (reversed) so it 100 | %% has to be reversed when we find \r\n or run out of characters 101 | 102 | get_next_word([$\r,$\n|T], L) -> {reverse([$\s|L]), T}; 103 | get_next_word([H|T], L) -> get_next_word(T, [H|L]); 104 | get_next_word([], L) -> {reverse([$\s|L]), []}. 105 | 106 | scan_trigrams([X,Y,Z], F, A) -> 107 | F([X,Y,Z], A); 108 | scan_trigrams([X,Y,Z|T], F, A) -> 109 | A1 = F([X,Y,Z], A), 110 | scan_trigrams([Y,Z|T], F, A1); 111 | scan_trigrams(_, _, A) -> 112 | A. 113 | 114 | 115 | %% access routines 116 | %% open() -> Table 117 | %% close(Table) 118 | %% is_word(Table, String) -> Bool 119 | 120 | 121 | is_word(Tab, Str) -> is_word1(Tab, "\s" ++ Str ++ "\s"). 122 | 123 | is_word1(Tab, [_,_,_]=X) -> is_this_a_trigram(Tab, X); 124 | is_word1(Tab, [A,B,C|D]) -> 125 | case is_this_a_trigram(Tab, [A,B,C]) of 126 | true -> is_word1(Tab, [B,C|D]); 127 | false -> false 128 | end; 129 | is_word1(_, _) -> 130 | false. 131 | 132 | is_this_a_trigram(Tab, X) -> 133 | case ets:lookup(Tab, list_to_binary(X)) of 134 | [] -> false; 135 | _ -> true 136 | end. 137 | 138 | open() -> 139 | {ok, I} = ets:file2tab(filename:dirname(code:which(?MODULE)) 140 | ++ "/trigramsS.tab"), 141 | I. 142 | 143 | close(Tab) -> ets:delete(Tab). 144 | 145 | -------------------------------------------------------------------------------- /code/lib_trigrams_complete.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(lib_trigrams). 3 | -export([for_each_trigram_in_the_english_language/2, 4 | make_tables/0, timer_tests/0, 5 | open/0, close/1, is_word/2, 6 | how_many_trigrams/0, 7 | make_ets_set/0, make_ets_ordered_set/0, make_mod_set/0, 8 | lookup_all_ets/2, lookup_all_set/2 9 | ]). 10 | -import(lists, [reverse/1]). 11 | 12 | 13 | 14 | make_tables() -> 15 | {Micro1, N} = timer:tc(?MODULE, how_many_trigrams, []), 16 | io:format("Counting - No of trigrams=~p time/trigram=~p~n",[N,Micro1/N]), 17 | {Micro2, Ntri} = timer:tc(?MODULE, make_ets_ordered_set, []), 18 | FileSize1 = filelib:file_size("trigramsOS.tab"), 19 | io:format("Ets ordered Set size=~p time/trigram=~p~n",[FileSize1/Ntri, 20 | Micro2/N]), 21 | {Micro3, _} = timer:tc(?MODULE, make_ets_set, []), 22 | FileSize2 = filelib:file_size("trigramsS.tab"), 23 | io:format("Ets set size=~p time/trigram=~p~n",[FileSize2/Ntri, Micro3/N]), 24 | {Micro4, _} = timer:tc(?MODULE, make_mod_set, []), 25 | FileSize3 = filelib:file_size("trigrams.set"), 26 | io:format("Module sets size=~p time/trigram=~p~n",[FileSize3/Ntri, Micro4/N]). 27 | 28 | 29 | 30 | make_ets_ordered_set() -> make_a_set(ordered_set, "trigramsOS.tab"). 31 | make_ets_set() -> make_a_set(set, "trigramsS.tab"). 32 | 33 | make_a_set(Type, FileName) -> 34 | Tab = ets:new(table, [Type]), 35 | F = fun(Str, _) -> ets:insert(Tab, {list_to_binary(Str)}) end, 36 | for_each_trigram_in_the_english_language(F, 0), 37 | ets:tab2file(Tab, FileName), 38 | Size = ets:info(Tab, size), 39 | ets:delete(Tab), 40 | Size. 41 | 42 | 43 | 44 | make_mod_set() -> 45 | D = sets:new(), 46 | F = fun(Str, Set) -> sets:add_element(list_to_binary(Str),Set) end, 47 | D1 = for_each_trigram_in_the_english_language(F, D), 48 | file:write_file("trigrams.set", [term_to_binary(D1)]). 49 | 50 | 51 | 52 | timer_tests() -> 53 | time_lookup_ets_set("Ets ordered Set", "trigramsOS.tab"), 54 | time_lookup_ets_set("Ets set", "trigramsS.tab"), 55 | time_lookup_module_sets(). 56 | 57 | 58 | time_lookup_ets_set(Type, File) -> 59 | {ok, Tab} = ets:file2tab(File), 60 | L = ets:tab2list(Tab), 61 | Size = length(L), 62 | {M, _} = timer:tc(?MODULE, lookup_all_ets, [Tab, L]), 63 | io:format("~s lookup=~p micro seconds~n",[Type, M/Size]), 64 | ets:delete(Tab). 65 | 66 | lookup_all_ets(Tab, L) -> 67 | lists:foreach(fun({K}) -> ets:lookup(Tab, K) end, L). 68 | 69 | time_lookup_module_sets() -> 70 | {ok, Bin} = file:read_file("trigrams.set"), 71 | Set = binary_to_term(Bin), 72 | Keys = sets:to_list(Set), 73 | Size = length(Keys), 74 | {M, _} = timer:tc(?MODULE, lookup_all_set, [Set, Keys]), 75 | io:format("Module set lookup=~p micro seconds~n",[M/Size]). 76 | 77 | lookup_all_set(Set, L) -> 78 | lists:foreach(fun(Key) -> sets:is_element(Key, Set) end, L). 79 | 80 | 81 | how_many_trigrams() -> 82 | F = fun(_, N) -> 1 + N end, 83 | for_each_trigram_in_the_english_language(F, 0). 84 | 85 | %% An iterator that iterates through all trigrams in the language 86 | 87 | for_each_trigram_in_the_english_language(F, A0) -> 88 | {ok, Bin0} = file:read_file("354984si.ngl.gz"), 89 | Bin = zlib:gunzip(Bin0), 90 | scan_word_list(binary_to_list(Bin), F, A0). 91 | 92 | scan_word_list([], _, A) -> 93 | A; 94 | scan_word_list(L, F, A) -> 95 | {Word, L1} = get_next_word(L, []), 96 | A1 = scan_trigrams([$\s|Word], F, A), 97 | scan_word_list(L1, F, A1). 98 | 99 | %% scan the word looking for \r\n 100 | %% the second argument is the word (reversed) so it 101 | %% has to be reversed when we find \r\n or run out of characters 102 | 103 | get_next_word([$\r,$\n|T], L) -> {reverse([$\s|L]), T}; 104 | get_next_word([H|T], L) -> get_next_word(T, [H|L]); 105 | get_next_word([], L) -> {reverse([$\s|L]), []}. 106 | 107 | scan_trigrams([X,Y,Z], F, A) -> 108 | F([X,Y,Z], A); 109 | 110 | 111 | scan_trigrams([X,Y,Z|T], F, A) -> 112 | A1 = F([X,Y,Z], A), 113 | scan_trigrams([Y,Z|T], F, A1); 114 | scan_trigrams(_, _, A) -> 115 | A. 116 | 117 | 118 | %% access routines 119 | %% open() -> Table 120 | %% close(Table) 121 | %% is_word(Table, String) -> Bool 122 | 123 | 124 | is_word(Tab, Str) -> is_word1(Tab, "\s" ++ Str ++ "\s"). 125 | 126 | is_word1(Tab, [_,_,_]=X) -> is_this_a_trigram(Tab, X); 127 | is_word1(Tab, [A,B,C|D]) -> 128 | case is_this_a_trigram(Tab, [A,B,C]) of 129 | true -> is_word1(Tab, [B,C|D]); 130 | false -> false 131 | end; 132 | is_word1(_, _) -> 133 | false. 134 | 135 | is_this_a_trigram(Tab, X) -> 136 | case ets:lookup(Tab, list_to_binary(X)) of 137 | [] -> false; 138 | _ -> true 139 | end. 140 | 141 | open() -> 142 | {ok, I} = ets:file2tab(filename:dirname(code:which(?MODULE)) 143 | ++ "/trigramsS.tab"), 144 | I. 145 | 146 | close(Tab) -> ets:delete(Tab). 147 | 148 | -------------------------------------------------------------------------------- /code/lists1.erl: -------------------------------------------------------------------------------- 1 | -module(lists1). 2 | -compile(export_all). 3 | 4 | %% Warning this module should be compiled for 5 | %% to check the systenmax of the modules only 6 | %% No beam code should be created 7 | 8 | map(_, []) -> []; 9 | map(F, [H|T]) -> [F(H)|map(F, T)]. 10 | 11 | 12 | 13 | member(H, [H|_]) -> true; 14 | member(H, [_|T]) -> member(H, T); 15 | member(_, []) -> false. 16 | 17 | 18 | 19 | partition(Pred, L) -> partition(Pred, L, [], []). 20 | 21 | partition(Pred, [H|T], Ts, Fs) -> 22 | case Pred(H) of 23 | true -> partition(Pred, T, [H|Ts], Fs); 24 | false -> partition(Pred, T, Ts, [H|Fs]) 25 | end; 26 | partition(_, [], Ts, Fs) -> 27 | {reverse(Ts), reverse(Fs)}. 28 | %END:partition 29 | 30 | 31 | reverse(L) -> reverse(L, []). 32 | 33 | reverse([H|T], L) -> reverse(T, [H|L]); 34 | reverse([], L) -> L. 35 | 36 | 37 | 38 | search(Key, [{Key,Val}|_]) -> {ok, Val}; %% (1) 39 | search(Key, [_|T]) -> search(Key, T); %% (2) 40 | search(_, []) -> error. %% (3) 41 | 42 | 43 | -------------------------------------------------------------------------------- /code/m1.erl: -------------------------------------------------------------------------------- 1 | -module(m1). 2 | -export([start/0]). 3 | 4 | -ifdef(debug). 5 | -define(TRACE(X), io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, X])). 6 | -else. 7 | -define(TRACE(X), void). 8 | -endif. 9 | 10 | start() -> loop(5). 11 | 12 | loop(0) -> 13 | void; 14 | loop(N) -> 15 | ?TRACE(N), 16 | loop(N-1). 17 | -------------------------------------------------------------------------------- /code/math1.erl: -------------------------------------------------------------------------------- 1 | -module(math1). %% (1) 2 | -export([double/1]). %% (2) 3 | 4 | double(X) -> 2*X. %% (3) 5 | 6 | -------------------------------------------------------------------------------- /code/math2.erl: -------------------------------------------------------------------------------- 1 | -module(math2). 2 | -export([map/2]). 3 | 4 | map(_, []) -> []; %% (1) 5 | map(F, [H|T]) -> [F(H)|map(F, T)]. %% (2) 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /code/math3.erl: -------------------------------------------------------------------------------- 1 | -module(math3). 2 | -export([area/1]). 3 | -import(math, [pi/0]). 4 | 5 | area({circle, Radius}) -> 6 | pi() * squared(Radius); 7 | area({triangle, A, B, C}) -> 8 | S = (A+B+C)/2, 9 | math:sqrt(S*(S-A)*(S-B)*(S-C)). 10 | 11 | squared(X) -> X*X. 12 | -------------------------------------------------------------------------------- /code/misc.erl: -------------------------------------------------------------------------------- 1 | -module(misc). 2 | -compile(export_all). 3 | 4 | perimeter({square, X}) -> 4 * X; 5 | perimeter({rectangle, Width, Ht}) -> 2 * (Width+Ht); 6 | perimeter({circle, R}) -> 2 * math:pi() * R; 7 | perimeter({triangle, A, B, C}) -> A + B + C. 8 | 9 | 10 | 11 | fac2(0) -> 1; 12 | fac2(N) when is_integer(N), N > 0 -> N * fac2(N-1); 13 | fac2(N) when is_integer(N), N < 0 -> exit({factorialNegativeArgument, N}); 14 | fac2(X) -> exit({factorialArgumentNotAnInteger, X}). 15 | 16 | 17 | 18 | fac1(0) -> 1; 19 | fac1(N) when N > 0 -> N * fac1(N-1). 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /code/monitor1.erl: -------------------------------------------------------------------------------- 1 | -module(monitor1). 2 | -export([start/1]). 3 | 4 | start(Pid) -> 5 | spawn(fun() -> run(Pid) end). 6 | 7 | run(Pid) -> 8 | link(Pid), 9 | loop(0). 10 | 11 | loop(N) -> 12 | io:format("~p N=~p~n",[?MODULE, N]), 13 | receive 14 | after 2000 -> 15 | loop(N+1) 16 | end. 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /code/monitor2.erl: -------------------------------------------------------------------------------- 1 | -module(monitor2). 2 | -export([start/0]). 3 | 4 | start() -> 5 | spawn(fun() -> run() end). 6 | 7 | run() -> 8 | process_flag(trap_exit, true), 9 | counter1:start(), 10 | link(whereis(counter1)), 11 | loop(). 12 | 13 | loop() -> 14 | receive 15 | {'EXIT', Pid, Why} -> 16 | io:format("Monitor caught an exit from:~p Why=~p~n", 17 | [Pid, Why]), 18 | counter1:start(), 19 | link(whereis(counter1)), 20 | loop() 21 | end. 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /code/monitor3.erl: -------------------------------------------------------------------------------- 1 | -module(monitor3). 2 | -export([start/0]). 3 | 4 | 5 | start() -> 6 | process_flag(trap_exit, true), 7 | Pid = spawn_link(fun() -> error1:start() end), 8 | loop(2, Pid). 9 | 10 | 11 | loop(N, Pid) -> 12 | io:format("~p N=~p~n",[?MODULE, N]), 13 | Pid ! N, 14 | receive 15 | Any -> 16 | io:format("~p received:~p~n",[?MODULE, Any]), 17 | loop(N, Pid) 18 | after 1000 -> 19 | loop(N-1, Pid) 20 | end. 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /code/motor_controller.erl: -------------------------------------------------------------------------------- 1 | -module(motor_controller). 2 | 3 | -export([add_event_handler/0]). 4 | 5 | add_event_handler() -> 6 | event_handler:add_handler(errors, fun controller/1). 7 | 8 | controller(too_hot) -> 9 | io:format("Turn off the motor~n"); 10 | controller(X) -> 11 | io:format("~w ignored event: ~p~n",[?MODULE, X]). 12 | -------------------------------------------------------------------------------- /code/mp3_manager.erl: -------------------------------------------------------------------------------- 1 | -module(mp3_manager). 2 | -import(lists, [map/2, reverse/1]). 3 | -compile(export_all). 4 | 5 | start1() -> 6 | Files = lib_files_find:files("/home/joe/music_keep", "*.mp3", true), 7 | V = map(fun handle/1, Files), 8 | lib_misc:dump("mp3data", V). 9 | 10 | start2() -> 11 | Files = lib_files_find:files("/windows/backup/music/artists", "*.mp3", true), 12 | V = map(fun handle/1, Files), 13 | lib_misc:dump("mp3data", V). 14 | 15 | handle(File) -> 16 | (catch read_id3_tag(File)). 17 | 18 | read_id3_tag(File) -> 19 | case file:open(File, [read,binary,raw]) of 20 | {ok, S} -> 21 | Size = filelib:file_size(File), 22 | Result = (catch analyse1(S, Size)), 23 | file:close(S), 24 | {File, Result}; 25 | Error -> 26 | {File, Error} 27 | end. 28 | 29 | analyse1(S, Size) -> 30 | {ok, B1} = file:pread(S, 0, 10), 31 | case parse_start_tag(B1) of 32 | {"ID3v2.3.0", {_Unsync, Extended, _Experimental, Len}} -> 33 | {ok, Data} = file:pread(S, 10, Len), 34 | case Extended of 35 | 1 -> 36 | {Extended, Data1} = split_binary(Data, 10), 37 | parse_frames(Data1); 38 | 0 -> 39 | parse_frames(Data) 40 | end; 41 | no -> 42 | %% see if we can find a tag at the end 43 | {ok, B2} = file:pread(S, Size-128, 128), 44 | parse_v1_tag(B2) 45 | end. 46 | 47 | parse_start_tag(<<$I,$D,$3,3,0,Unsync:1,Extended:1,Experimental:1,_:5,K:32>>) -> 48 | Tag = "ID3v2.3.0", 49 | Size = syncsafe2int(K), 50 | {Tag, {Unsync, Extended, Experimental, Size}}; 51 | parse_start_tag(_) -> 52 | no. 53 | 54 | parse_frames(B) -> 55 | F = gather_frames(B), 56 | G = map(fun parse_frame/1, F), 57 | H = [{I,J}||{I,J}<-G], 58 | {ok, H}. 59 | 60 | parse_frame({"TIT2", _, Txt}) -> {title, parse_txt(Txt)}; 61 | parse_frame({"TPE1", _, Txt}) -> {performer, parse_txt(Txt)}; 62 | parse_frame({"TALB", _, Txt}) -> {album, parse_txt(Txt)}; 63 | parse_frame({"TRCK", _, Txt}) -> {track, parse_txt(Txt)}; 64 | parse_frame(_) -> skipped. 65 | 66 | parse_txt(<<0:8,Txt/binary>>) -> 67 | Txt; 68 | parse_txt(<<1:8,16#ff,16#fe, Txt/binary>>) -> 69 | unicode_to_ascii(Txt). 70 | 71 | unicode_to_ascii(Bin) -> list_to_binary(uni_to_ascii1(binary_to_list(Bin))). 72 | 73 | uni_to_ascii1([X,_|Y]) -> [X|uni_to_ascii1(Y)]; 74 | uni_to_ascii1([]) -> []. 75 | 76 | gather_frames(B) when size(B) < 10 -> 77 | []; 78 | gather_frames(<<0,0,0,0,_/binary>>) -> 79 | []; 80 | gather_frames(<<$P,$R,$I,$V,_/binary>>) -> 81 | []; 82 | gather_frames(<>) -> 83 | <<_A:1,_B:1,_C:1,_:5,I:1,J:1,_K:1,_:5>> = <>, 84 | case {I, J} of 85 | {0, 0} -> 86 | Tag = [Id1,Id2,Id3,Id4], 87 | case is_tag(Tag) of 88 | true -> 89 | Size = syncsafe2int(SafeN), 90 | {Data, Next} = split_binary(Rest, Size), 91 | [{Tag,Flags,Data}|gather_frames(Next)]; 92 | false -> 93 | [] 94 | end; 95 | _ -> 96 | %% bad flags 97 | [] 98 | end; 99 | gather_frames(CC) -> 100 | [{error, CC}]. 101 | 102 | is_tag([H|T]) when $A =< H, H =< $Z -> is_tag(T); 103 | is_tag([H|T]) when $0 =< H, H =< $9 -> is_tag(T); 104 | is_tag([]) -> true; 105 | is_tag(_) -> false. 106 | 107 | 108 | syncsafe2int(N) -> 109 | <<_:1,N1:7,_:1,N2:7,_:1,N3:7,_:1,N4:7>> = <>, 110 | <> = <<0:4,N1:7,N2:7,N3:7,N4:7>>, 111 | I. 112 | 113 | parse_v1_tag(<<$T,$A,$G,B/binary>>) -> 114 | {Title, B1} = split_binary(B, 30), 115 | {Artist, B2} = split_binary(B1, 30), 116 | {Album, B3} = split_binary(B2, 30), 117 | {_Year, B4} = split_binary(B3, 4), 118 | {_Comment, <>} = split_binary(B4, 28), 119 | L = [{title,trim(Title)},{artist,trim(Artist)}, {album, trim(Album)}], 120 | case K of 121 | 0 -> 122 | {"ID3v1.1", [{track,Track}|L]}; 123 | _ -> 124 | {"ID3v1", L} 125 | end; 126 | parse_v1_tag(_) -> 127 | no. 128 | 129 | trim(Bin) -> 130 | list_to_binary(trim_blanks(binary_to_list(Bin))). 131 | 132 | trim_blanks(X) -> reverse(skip_blanks_and_zero(reverse(X))). 133 | 134 | skip_blanks_and_zero([$\s|T]) -> skip_blanks_and_zero(T); 135 | skip_blanks_and_zero([0|T]) -> skip_blanks_and_zero(T); 136 | skip_blanks_and_zero(X) -> X. 137 | -------------------------------------------------------------------------------- /code/mp3_sync.erl: -------------------------------------------------------------------------------- 1 | -module(mp3_sync). 2 | 3 | -export([find_sync/2]). 4 | 5 | %% find_sync(Bin, N) -> {ok, K} | error. 6 | 7 | %% Bin is a continuous block of MP3 data. 8 | %% At some offset K in Bin we can find the start of 9 | %% an MPEG header 10 | %% Start searching at byte N in Bin 11 | %% and look for the next word K where we 12 | %% are synced - require three in a row to achieve sync 13 | 14 | 15 | find_sync(Bin, N) -> 16 | case is_header(N, Bin) of 17 | {ok, Len1, _} -> 18 | case is_header(N + Len1, Bin) of 19 | {ok, Len2, _} -> 20 | case is_header(N + Len1 + Len2, Bin) of 21 | {ok, _, _} -> 22 | {ok, N}; 23 | error -> 24 | find_sync(Bin, N+1) 25 | end; 26 | error -> 27 | find_sync(Bin, N+1) 28 | end; 29 | error -> 30 | find_sync(Bin, N+1) 31 | end. 32 | 33 | %% is_header(N, Bin) -> {ok, FrameLength, Info} | error. 34 | 35 | 36 | is_header(N, Bin) -> 37 | unpack_header(get_word(N, Bin)). 38 | 39 | get_word(N, Bin) -> 40 | {_,<>} = split_binary(Bin, N), 41 | C. 42 | 43 | unpack_header(X) -> 44 | try decode_header(X) 45 | catch 46 | _:_ -> error 47 | end. 48 | 49 | 50 | 51 | decode_header(<<2#11111111111:11,B:2,C:2,_D:1,E:4,F:2,G:1,Bits:9>>) -> 52 | Vsn = case B of 53 | 0 -> {2,5}; 54 | 1 -> exit(badVsn); 55 | 2 -> 2; 56 | 3 -> 1 57 | end, 58 | Layer = case C of 59 | 0 -> exit(badLayer); 60 | 1 -> 3; 61 | 2 -> 2; 62 | 3 -> 1 63 | end, 64 | %% Protection = D, 65 | BitRate = bitrate(Vsn, Layer, E) * 1000, 66 | SampleRate = samplerate(Vsn, F), 67 | Padding = G, 68 | FrameLength = framelength(Layer, BitRate, SampleRate, Padding), 69 | if 70 | FrameLength < 21 -> 71 | exit(frameSize); 72 | true -> 73 | {ok, FrameLength, {Layer,BitRate,SampleRate,Vsn,Bits}} 74 | end; 75 | decode_header(_) -> 76 | exit(badHeader). 77 | 78 | 79 | bitrate(_,_,15) -> exit(1); 80 | bitrate(1,1,E) -> 81 | element(E+1, {free,32,64,96,128,160,192,224,256,288, 82 | 320,352,384,416,448}); 83 | bitrate(1,2,E) -> 84 | element(E+1, {free,32,48,56,64,80,96,112,128,160, 85 | 192,224,256,320,384}); 86 | bitrate(1,3,E) -> 87 | element(E+1, {free,32,40,48,56,64,80,96,112,128,160,192, 88 | 224,256,320}); 89 | bitrate(2,1,E) -> 90 | element(E+1, {free,32,48,56,64,80,96,112,128,144,160, 91 | 176,192,224,256}); 92 | bitrate(2,2,E) -> 93 | element(E+1, {free,8,16,24,32,40,48,56,64,80,96,112, 94 | 128,144,160}); 95 | bitrate(2,3,E) -> bitrate(2,2,E); 96 | bitrate({2,5}, L, E) -> bitrate(2, L, E). 97 | 98 | %% samplerate Vsn F 99 | samplerate(1, 0) -> 44100; 100 | samplerate(1, 1) -> 48000; 101 | samplerate(1, 2) -> 32000; 102 | samplerate(2, 0) -> 22050; 103 | samplerate(2, 1) -> 24000; 104 | samplerate(2, 2) -> 16000; 105 | samplerate({2,5}, 0) -> 11025; 106 | samplerate({2,5}, 1) -> 12000; 107 | samplerate({2,5}, 2) -> 8000. 108 | 109 | framelength(1, BitRate, SampleRate, Padding) -> 110 | ((12*BitRate div SampleRate) + Padding) * 4; 111 | framelength(_, BitRate, SampleRate, Padding) -> 112 | (144 * BitRate div SampleRate) + Padding. 113 | 114 | -------------------------------------------------------------------------------- /code/my_alarm_handler.erl: -------------------------------------------------------------------------------- 1 | -module(my_alarm_handler). 2 | -behaviour(gen_event). 3 | 4 | %% gen_event callbacks 5 | -export([init/1, handle_event/2, handle_call/2, 6 | handle_info/2, terminate/2]). 7 | 8 | %% init(Args) must return {ok, State} 9 | init(Args) -> 10 | io:format("*** my_alarm_handler init:~p~n",[Args]), 11 | {ok, 0}. 12 | 13 | handle_event({set_alarm, tooHot}, N) -> 14 | error_logger:error_msg("*** Tell the Engineer to turn on the fan~n"), 15 | {ok, N+1}; 16 | handle_event({clear_alarm, tooHot}, N) -> 17 | error_logger:error_msg("*** Danger over. Turn off the fan~n"), 18 | {ok, N}; 19 | handle_event(Event, N) -> 20 | io:format("*** unmatched event:~p~n",[Event]), 21 | {ok, N}. 22 | 23 | handle_call(_Request, N) -> Reply = N, {ok, Reply, N}. 24 | 25 | handle_info(_Info, N) -> {ok, N}. 26 | 27 | terminate(_Reason, _N) -> ok. 28 | -------------------------------------------------------------------------------- /code/my_bank.erl: -------------------------------------------------------------------------------- 1 | -module(my_bank). 2 | 3 | -behaviour(gen_server). 4 | -export([start/0]). 5 | %% gen_server callbacks 6 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 7 | terminate/2, code_change/3]). 8 | -compile(export_all). 9 | 10 | 11 | start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 12 | stop() -> gen_server:call(?MODULE, stop). 13 | 14 | new_account(Who) -> gen_server:call(?MODULE, {new, Who}). 15 | deposit(Who, Amount) -> gen_server:call(?MODULE, {add, Who, Amount}). 16 | withdraw(Who, Amount) -> gen_server:call(?MODULE, {remove, Who, Amount}). 17 | 18 | 19 | 20 | init([]) -> {ok, ets:new(?MODULE,[])}. 21 | 22 | handle_call({new,Who}, _From, Tab) -> 23 | Reply = case ets:lookup(Tab, Who) of 24 | [] -> ets:insert(Tab, {Who,0}), 25 | {welcome, Who}; 26 | [_] -> {Who, you_already_are_a_customer} 27 | end, 28 | {reply, Reply, Tab}; 29 | handle_call({add,Who,X}, _From, Tab) -> 30 | Reply = case ets:lookup(Tab, Who) of 31 | [] -> not_a_customer; 32 | [{Who,Balance}] -> 33 | NewBalance = Balance + X, 34 | ets:insert(Tab, {Who, NewBalance}), 35 | {thanks, Who, your_balance_is, NewBalance} 36 | end, 37 | {reply, Reply, Tab}; 38 | handle_call({remove,Who, X}, _From, Tab) -> 39 | Reply = case ets:lookup(Tab, Who) of 40 | [] -> not_a_customer; 41 | [{Who,Balance}] when X =< Balance -> 42 | NewBalance = Balance - X, 43 | ets:insert(Tab, {Who, NewBalance}), 44 | {thanks, Who, your_balance_is, NewBalance}; 45 | [{Who,Balance}] -> 46 | {sorry,Who,you_only_have,Balance,in_the_bank} 47 | end, 48 | {reply, Reply, Tab}; 49 | handle_call(stop, _From, Tab) -> 50 | {stop, normal, stopped, Tab}. 51 | 52 | handle_cast(_Msg, State) -> {noreply, State}. 53 | handle_info(_Info, State) -> {noreply, State}. 54 | terminate(_Reason, _State) -> ok. 55 | code_change(_OldVsn, State, Extra) -> {ok, State}. 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /code/my_fac_server.erl: -------------------------------------------------------------------------------- 1 | -module(my_fac_server). 2 | -export([loop/0]). 3 | 4 | loop() -> 5 | receive 6 | {From, {fac, N}} -> 7 | From ! {self(), fac(N)}, 8 | loop(); 9 | {become, Something} -> 10 | Something() 11 | end. 12 | 13 | fac(0) -> 1; 14 | fac(N) -> N * fac(N-1). 15 | -------------------------------------------------------------------------------- /code/mylists.erl: -------------------------------------------------------------------------------- 1 | -module(mylists). 2 | 3 | -compile(export_all). 4 | %% Some definitions copied from lists.erl 5 | %% for pedagogic purposes 6 | 7 | 8 | map(_, []) -> []; %% (1) 9 | map(F, [H|T]) -> [F(H)|map(F, T)]. %% (2) 10 | 11 | 12 | 13 | member(H, [H|_]) -> true; 14 | member(H, [_|T]) -> member(H, T); 15 | member(_, []) -> false. 16 | 17 | 18 | 19 | sum([H|T]) -> H + sum(T); %% (3) 20 | sum([]) -> 0. %% (4) 21 | 22 | 23 | 24 | partition(Pred, L) -> partition(Pred, L, [], []). 25 | 26 | partition(Pred, [H|T], Ts, Fs) -> 27 | case Pred(H) of 28 | true -> partition(Pred, T, [H|Ts], Fs); 29 | false -> partition(Pred, T, Ts, [H|Fs]) 30 | end; 31 | partition(_, [], Ts, Fs) -> 32 | {reverse(Ts), reverse(Fs)}. 33 | %END:partition 34 | 35 | 36 | reverse(L) -> reverse(L, []). 37 | 38 | reverse([H|T], L) -> reverse(T, [H|L]); 39 | reverse([], L) -> L. 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /code/name_server.erl: -------------------------------------------------------------------------------- 1 | -module(name_server). 2 | -export([init/0, add/2, whereis/1, handle/2]). 3 | -import(server1, [rpc/2]). 4 | 5 | %% client routines 6 | add(Name, Place) -> rpc(name_server, {add, Name, Place}). 7 | whereis(Name) -> rpc(name_server, {whereis, Name}). 8 | 9 | %% callback routines 10 | init() -> dict:new(). 11 | 12 | handle({add, Name, Place}, Dict) -> {ok, dict:store(Name, Place, Dict)}; 13 | handle({whereis, Name}, Dict) -> {dict:find(Name, Dict), Dict}. 14 | 15 | -------------------------------------------------------------------------------- /code/name_server1.erl: -------------------------------------------------------------------------------- 1 | -module(name_server1). 2 | -export([init/0, add/2, whereis/1, handle/2]). 3 | -import(server3, [rpc/2]). 4 | 5 | %% client routines 6 | add(Name, Place) -> rpc(name_server, {add, Name, Place}). 7 | whereis(Name) -> rpc(name_server, {whereis, Name}). 8 | 9 | %% callback routines 10 | init() -> dict:new(). 11 | 12 | handle({add, Name, Place}, Dict) -> {ok, dict:store(Name, Place, Dict)}; 13 | handle({whereis, Name}, Dict) -> {dict:find(Name, Dict), Dict}. 14 | -------------------------------------------------------------------------------- /code/new_name_server.erl: -------------------------------------------------------------------------------- 1 | -module(new_name_server). 2 | -export([init/0, add/2, all_names/0, delete/1, whereis/1, handle/2]). 3 | -import(server3, [rpc/2]). 4 | 5 | %% interface 6 | all_names() -> rpc(name_server, allNames). 7 | add(Name, Place) -> rpc(name_server, {add, Name, Place}). 8 | delete(Name) -> rpc(name_server, {delete, Name}). 9 | whereis(Name) -> rpc(name_server, {whereis, Name}). 10 | 11 | %% callback routines 12 | init() -> dict:new(). 13 | 14 | handle({add, Name, Place}, Dict) -> {ok, dict:store(Name, Place, Dict)}; 15 | handle(allNames, Dict) -> {dict:fetch_keys(Dict), Dict}; 16 | handle({delete, Name}, Dict) -> {ok, dict:erase(Name, Dict)}; 17 | handle({whereis, Name}, Dict) -> {dict:find(Name, Dict), Dict}. 18 | -------------------------------------------------------------------------------- /code/phofs.erl: -------------------------------------------------------------------------------- 1 | -module(phofs). 2 | -export([mapreduce/4]). 3 | 4 | -import(lists, [foreach/2]). 5 | 6 | %% F1(Pid, X) -> sends {Key,Val} messages to Pid 7 | %% F2(Key, [Val], AccIn) -> AccOut 8 | 9 | mapreduce(F1, F2, Acc0, L) -> 10 | S = self(), 11 | Pid = spawn(fun() -> reduce(S, F1, F2, Acc0, L) end), 12 | receive 13 | {Pid, Result} -> 14 | Result 15 | end. 16 | 17 | reduce(Parent, F1, F2, Acc0, L) -> 18 | process_flag(trap_exit, true), 19 | ReducePid = self(), 20 | %% Create the Map processes 21 | %% One for each element X in L 22 | foreach(fun(X) -> 23 | spawn_link(fun() -> do_job(ReducePid, F1, X) end) 24 | end, L), 25 | N = length(L), 26 | %% make a dictionary to store the Keys 27 | Dict0 = dict:new(), 28 | %% Wait for N Map processes to terminate 29 | Dict1 = collect_replies(N, Dict0), 30 | Acc = dict:fold(F2, Acc0, Dict1), 31 | Parent ! {self(), Acc}. 32 | 33 | %% collect_replies(N, Dict) 34 | %% collect and merge {Key, Value} messages from N processes. 35 | %% When N processes have terminated return a dictionary 36 | %% of {Key, [Value]} pairs 37 | 38 | collect_replies(0, Dict) -> 39 | Dict; 40 | collect_replies(N, Dict) -> 41 | receive 42 | {Key, Val} -> 43 | case dict:is_key(Key, Dict) of 44 | true -> 45 | Dict1 = dict:append(Key, Val, Dict), 46 | collect_replies(N, Dict1); 47 | false -> 48 | Dict1 = dict:store(Key,[Val], Dict), 49 | collect_replies(N, Dict1) 50 | end; 51 | {'EXIT', _, _Why} -> 52 | collect_replies(N-1, Dict) 53 | end. 54 | 55 | %% Call F(Pid, X) 56 | %% F must send {Key, Value} messsages to Pid 57 | %% and then terminate 58 | 59 | do_job(ReducePid, F, X) -> 60 | F(ReducePid, X). 61 | -------------------------------------------------------------------------------- /code/ports/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .erl .beam .yrl 2 | 3 | .erl.beam: 4 | 5 | erlc -W $< 6 | 7 | MODS = example1 example1_lid 8 | 9 | all: ${MODS:%=%.beam} example1 example1_drv.so 10 | 11 | example1: example1.c erl_comm.c example1_driver.c 12 | gcc -o example1 example1.c erl_comm.c example1_driver.c 13 | 14 | example1_drv.so: example1_lid.c example1.c 15 | gcc -o example1_drv.so -fpic -shared example1.c example1_lid.c 16 | 17 | clean: 18 | rm example1 example1_drv.so *.beam 19 | 20 | -------------------------------------------------------------------------------- /code/ports/erl_comm.c: -------------------------------------------------------------------------------- 1 | /* erl_comm.c */ 2 | #include 3 | 4 | typedef unsigned char byte; 5 | 6 | int read_cmd(byte *buf); 7 | int write_cmd(byte *buf, int len); 8 | int read_exact(byte *buf, int len); 9 | int write_exact(byte *buf, int len); 10 | 11 | int read_cmd(byte *buf) 12 | { 13 | int len; 14 | 15 | if (read_exact(buf, 2) != 2) 16 | return(-1); 17 | len = (buf[0] << 8) | buf[1]; 18 | return read_exact(buf, len); 19 | } 20 | 21 | int write_cmd(byte *buf, int len) 22 | { 23 | byte li; 24 | 25 | li = (len >> 8) & 0xff; 26 | write_exact(&li, 1); 27 | 28 | li = len & 0xff; 29 | write_exact(&li, 1); 30 | 31 | return write_exact(buf, len); 32 | } 33 | 34 | int read_exact(byte *buf, int len) 35 | { 36 | int i, got=0; 37 | 38 | do { 39 | if ((i = read(0, buf+got, len-got)) <= 0) 40 | return(i); 41 | got += i; 42 | } while (got 6 | spawn(fun() -> 7 | register(example1, self()), 8 | process_flag(trap_exit, true), 9 | Port = open_port({spawn, "./example1"}, [{packet, 2}]), 10 | loop(Port) 11 | end). 12 | 13 | stop() -> 14 | example1 ! stop. 15 | 16 | twice(X) -> call_port({twice, X}). 17 | sum(X,Y) -> call_port({sum, X, Y}). 18 | 19 | call_port(Msg) -> 20 | example1 ! {call, self(), Msg}, 21 | receive 22 | {example1, Result} -> 23 | Result 24 | end. 25 | 26 | loop(Port) -> 27 | receive 28 | {call, Caller, Msg} -> 29 | Port ! {self(), {command, encode(Msg)}}, 30 | receive 31 | {Port, {data, Data}} -> 32 | Caller ! {example1, decode(Data)} 33 | end, 34 | loop(Port); 35 | stop -> 36 | Port ! {self(), close}, 37 | receive 38 | {Port, closed} -> 39 | exit(normal) 40 | end; 41 | {'EXIT', Port, Reason} -> 42 | exit({port_terminated,Reason}) 43 | end. 44 | 45 | encode({twice, X}) -> [1, X]; 46 | encode({sum, X, Y}) -> [2, X, Y]. 47 | 48 | decode([Int]) -> Int. 49 | -------------------------------------------------------------------------------- /code/ports/example1_driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | typedef unsigned char byte; 3 | 4 | int read_cmd(byte *buff); 5 | int write_cmd(byte *buff, int len); 6 | 7 | int main() { 8 | int fn, arg1, arg2, result; 9 | byte buff[100]; 10 | 11 | while (read_cmd(buff) > 0) { 12 | fn = buff[0]; 13 | 14 | if (fn == 1) { 15 | arg1 = buff[1]; 16 | result = twice(arg1); 17 | } else if (fn == 2) { 18 | arg1 = buff[1]; 19 | arg2 = buff[2]; 20 | /* debug -- you can print to stderr to debug 21 | fprintf(stderr,"calling sum %i %i\n",arg1,arg2); */ 22 | result = sum(arg1, arg2); 23 | } 24 | 25 | buff[0] = result; 26 | write_cmd(buff, 1); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /code/ports/example1_lid.c: -------------------------------------------------------------------------------- 1 | /* example1_lid.c */ 2 | 3 | #include 4 | #include "erl_driver.h" 5 | 6 | typedef struct { 7 | ErlDrvPort port; 8 | } example_data; 9 | 10 | static ErlDrvData example_drv_start(ErlDrvPort port, char *buff) 11 | { 12 | example_data* d = (example_data*)driver_alloc(sizeof(example_data)); 13 | d->port = port; 14 | return (ErlDrvData)d; 15 | } 16 | 17 | static void example_drv_stop(ErlDrvData handle) 18 | { 19 | driver_free((char*)handle); 20 | } 21 | 22 | static void example_drv_output(ErlDrvData handle, char *buff, int bufflen) 23 | { 24 | example_data* d = (example_data*)handle; 25 | char fn = buff[0], arg = buff[1], res; 26 | if (fn == 1) { 27 | res = twice(arg); 28 | } else if (fn == 2) { 29 | res = sum(buff[1], buff[2]); 30 | } 31 | driver_output(d->port, &res, 1); 32 | } 33 | 34 | ErlDrvEntry example_driver_entry = { 35 | NULL, /* F_PTR init, N/A */ 36 | example_drv_start, /* L_PTR start, called when port is opened */ 37 | example_drv_stop, /* F_PTR stop, called when port is closed */ 38 | example_drv_output, /* F_PTR output, called when erlang has sent 39 | data to the port */ 40 | NULL, /* F_PTR ready_input, 41 | called when input descriptor ready to read*/ 42 | NULL, /* F_PTR ready_output, 43 | called when output descriptor ready to write */ 44 | "example1_drv", /* char *driver_name, the argument to open_port */ 45 | NULL, /* F_PTR finish, called when unloaded */ 46 | NULL, /* F_PTR control, port_command callback */ 47 | NULL, /* F_PTR timeout, reserved */ 48 | NULL /* F_PTR outputv, reserved */ 49 | }; 50 | 51 | DRIVER_INIT(example_drv) /* must match name in driver_entry */ 52 | { 53 | return &example_driver_entry; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /code/ports/example1_lid.erl: -------------------------------------------------------------------------------- 1 | -module(example1_lid). 2 | -export([start/0, stop/0]). 3 | -export([twice/1, sum/2]). 4 | 5 | start() -> 6 | start("example1_drv"). 7 | 8 | start(SharedLib) -> 9 | case erl_ddll:load_driver(".", SharedLib) of 10 | ok -> ok; 11 | {error, already_loaded} -> ok; 12 | _ -> exit({error, could_not_load_driver}) 13 | end, 14 | spawn(fun() -> init(SharedLib) end). 15 | 16 | init(SharedLib) -> 17 | register(example1_lid, self()), 18 | Port = open_port({spawn, SharedLib}, []), 19 | loop(Port). 20 | 21 | stop() -> 22 | example1_lid ! stop. 23 | 24 | twice(X) -> call_port({twice, X}). 25 | sum(X,Y) -> call_port({sum, X, Y}). 26 | 27 | call_port(Msg) -> 28 | example1_lid ! {call, self(), Msg}, 29 | receive 30 | {example1_lid, Result} -> 31 | Result 32 | end. 33 | 34 | loop(Port) -> 35 | receive 36 | {call, Caller, Msg} -> 37 | Port ! {self(), {command, encode(Msg)}}, 38 | receive 39 | {Port, {data, Data}} -> 40 | Caller ! {example1_lid, decode(Data)} 41 | end, 42 | loop(Port); 43 | stop -> 44 | Port ! {self(), close}, 45 | receive 46 | {Port, closed} -> 47 | exit(normal) 48 | end; 49 | {'EXIT', Port, Reason} -> 50 | io:format("~p ~n", [Reason]), 51 | exit(port_terminated) 52 | end. 53 | 54 | encode({twice, X}) -> [1, X]; 55 | encode({sum, X, Y}) -> [2, X, Y]. 56 | 57 | decode([Int]) -> Int. 58 | -------------------------------------------------------------------------------- /code/ports/log: -------------------------------------------------------------------------------- 1 | To build the first example we need complex.c port.c and erl_comm.c 2 | 3 | gcc -o extprg complex.c erl_comm.c port.c 4 | 5 | 1> complex1:start("extprg"). 6 | <0.42.0> 7 | 2> complex1:bar(5). 8 | 10 9 | 3> complex1:foo(5). 10 | 6 11 | 4> complex1:stop. 12 | ** 1: illegal expression ** 13 | 5> complex1:stop(). 14 | stop 15 | 16 | (solaris) 17 | gcc -shared -o example_drv.so -fpic complex.c port_driver.c 18 | 19 | complex5:start("example_drv"). 20 | <0.44.0> 21 | complex5:foo(8). 22 | 23 | 24 | -------------------------------------------------------------------------------- /code/ports/port_driver.c: -------------------------------------------------------------------------------- 1 | /* example1_port_driver.c */ 2 | 3 | #include 4 | #include "erl_driver.h" 5 | 6 | typedef struct { 7 | ErlDrvPort port; 8 | } example_data; 9 | 10 | static ErlDrvData example_drv_start(ErlDrvPort port, char *buff) 11 | { 12 | example_data* d = (example_data*)driver_alloc(sizeof(example_data)); 13 | d->port = port; 14 | return (ErlDrvData)d; 15 | } 16 | 17 | static void example_drv_stop(ErlDrvData handle) 18 | { 19 | driver_free((char*)handle); 20 | } 21 | 22 | static void example_drv_output(ErlDrvData handle, char *buff, int bufflen) 23 | { 24 | example_data* d = (example_data*)handle; 25 | char fn = buff[0], arg = buff[1], res; 26 | if (fn == 1) { 27 | res = twice(arg); 28 | } else if (fn == 2) { 29 | res = sum(buff[1], buff[2]); 30 | } 31 | driver_output(d->port, &res, 1); 32 | } 33 | 34 | ErlDrvEntry example_driver_entry = { 35 | NULL, /* F_PTR init, N/A */ 36 | example_drv_start, /* L_PTR start, called when port is opened */ 37 | example_drv_stop, /* F_PTR stop, called when port is closed */ 38 | example_drv_output, /* F_PTR output, called when erlang has sent */ 39 | NULL, /* F_PTR ready_input, called when input descriptor ready */ 40 | NULL, /* F_PTR ready_output, called when output descriptor ready */ 41 | "example1_drv", /* char *driver_name, the argument to open_port */ 42 | NULL, /* F_PTR finish, called when unloaded */ 43 | NULL, /* F_PTR control, port_command callback */ 44 | NULL, /* F_PTR timeout, reserved */ 45 | NULL /* F_PTR outputv, reserved */ 46 | }; 47 | 48 | DRIVER_INIT(example_drv) /* must match name in driver_entry */ 49 | { 50 | return &example_driver_entry; 51 | } 52 | -------------------------------------------------------------------------------- /code/prime_server.erl: -------------------------------------------------------------------------------- 1 | -module(prime_server). 2 | -behaviour(gen_server). 3 | 4 | -export([new_prime/1, start_link/0]). 5 | 6 | %% gen_server callbacks 7 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 8 | terminate/2, code_change/3]). 9 | 10 | start_link() -> 11 | gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). 12 | 13 | new_prime(N) -> 14 | %% 20000 is a timeout (ms) 15 | gen_server:call(?MODULE, {prime, N}, 20000). 16 | 17 | init([]) -> 18 | %% Note we must set trap_exit = true if we 19 | %% want terminate/2 to be called when the application 20 | %% is stopped 21 | process_flag(trap_exit, true), 22 | io:format("~p starting~n",[?MODULE]), 23 | {ok, 0}. 24 | 25 | handle_call({prime, K}, _From, N) -> 26 | {reply, make_new_prime(K), N+1}. 27 | 28 | handle_cast(_Msg, N) -> {noreply, N}. 29 | 30 | handle_info(_Info, N) -> {noreply, N}. 31 | 32 | terminate(_Reason, _N) -> 33 | io:format("~p stopping~n",[?MODULE]), 34 | ok. 35 | 36 | code_change(_OldVsn, N, _Extra) -> {ok, N}. 37 | 38 | make_new_prime(K) -> 39 | if 40 | K > 100 -> 41 | alarm_handler:set_alarm(tooHot), 42 | N = lib_primes:make_prime(K), 43 | alarm_handler:clear_alarm(tooHot), 44 | N; 45 | true -> 46 | lib_primes:make_prime(K) 47 | end. 48 | 49 | 50 | -------------------------------------------------------------------------------- /code/processes.erl: -------------------------------------------------------------------------------- 1 | -module(processes). 2 | 3 | -export([max/1]). 4 | 5 | %% max(N) 6 | %% Create N processes then destroy them 7 | %% See how much time this takes 8 | 9 | max(N) -> 10 | Max = erlang:system_info(process_limit), 11 | io:format("Maximum allowed processes:~p~n",[Max]), 12 | statistics(runtime), 13 | statistics(wall_clock), 14 | L = for(1, N, fun() -> spawn(fun() -> wait() end) end), 15 | {_, Time1} = statistics(runtime), 16 | {_, Time2} = statistics(wall_clock), 17 | lists:foreach(fun(Pid) -> Pid ! die end, L), 18 | U1 = Time1 * 1000 / N, 19 | U2 = Time2 * 1000 / N, 20 | io:format("Process spawn time=~p (~p) microseconds~n", 21 | [U1, U2]). 22 | 23 | wait() -> 24 | receive 25 | die -> void 26 | end. 27 | 28 | for(N, N, F) -> [F()]; 29 | for(I, N, F) -> [F()|for(I+1, N, F)]. 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /code/ptests.erl: -------------------------------------------------------------------------------- 1 | -module(ptests). 2 | -export([tests/1, fib/1]). 3 | -import(lists, [map/2]). 4 | -import(lib_misc, [pmap/2]). 5 | 6 | tests([N]) -> 7 | Nsched = list_to_integer(atom_to_list(N)), 8 | run_tests(1, Nsched). 9 | 10 | run_tests(N, Nsched) -> 11 | case test(N) of 12 | stop -> 13 | init:stop(); 14 | Val -> 15 | io:format("~p.~n",[{Nsched, Val}]), 16 | run_tests(N+1, Nsched) 17 | end. 18 | 19 | test(1) -> 20 | %% Make 100 lists 21 | %% Each list contains 1000 random integers 22 | seed(), 23 | S = lists:seq(1,100), 24 | L = map(fun(_) -> mkList(1000) end, S), 25 | {Time1, S1} = timer:tc(lists, map, [fun lists:sort/1, L]), 26 | {Time2, S2} = timer:tc(lib_misc, pmap, [fun lists:sort/1, L]), 27 | {sort, Time1, Time2, equal(S1, S2)}; 28 | test(2) -> 29 | %% L = [27,27,27,..] 100 times 30 | L = lists:duplicate(100, 27), 31 | {Time1, S1} = timer:tc(lists, map, [fun ptests:fib/1, L]), 32 | {Time2, S2} = timer:tc(lib_misc, pmap, [fun ptests:fib/1, L]), 33 | {fib, Time1, Time2, equal(S1, S2)}; 34 | test(3) -> 35 | stop. 36 | 37 | %% Equal is used to test that map and pmap compute the same thing 38 | equal(S,S) -> true; 39 | equal(S1,S2) -> {differ, S1, S2}. 40 | 41 | %% recursive (inefficent) fibonacci 42 | fib(0) -> 1; 43 | fib(1) -> 1; 44 | fib(N) -> fib(N-1) + fib(N-2). 45 | 46 | %% Reset the random number generator. This is so we 47 | %% get the same sequence of random numbers each time we run 48 | %% the program 49 | 50 | seed() -> random:seed(44,55,66). 51 | 52 | %% Make a list of K random numbers 53 | %% Each random number in the range 1..1000000 54 | mkList(K) -> mkList(K, []). 55 | 56 | mkList(0, L) -> L; 57 | mkList(N, L) -> mkList(N-1, [random:uniform(1000000)|L]). 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /code/readme.trigrams: -------------------------------------------------------------------------------- 1 | The file 354984si.ngl contains 2 | Over 354,000 single words. These come from the Moby project 3 | 4 | See 5 | 6 | http://www.dcs.shef.ac.uk/research/ilash/Moby/ 7 | 8 | << 9 | On June 1, 1996 Grady Ward announced that the fruits of the Moby 10 | project were being placed in the public domain: 11 | 12 | The Moby lexicon project is complete and has 13 | been place into the public domain. Use, sell, 14 | rework, excerpt and use in any way on any platform. 15 | 16 | Placing this material on internal or public servers is 17 | also encouraged. The compiler is not aware of any 18 | export restrictions so freely distribute world-wide. 19 | 20 | You can verify the public domain status by contacting 21 | 22 | Grady Ward 23 | 3449 Martha Ct. 24 | Arcata, CA 95521-4884 25 | 26 | daedal@myrealbox.com 27 | >> -------------------------------------------------------------------------------- /code/records.hrl: -------------------------------------------------------------------------------- 1 | -record(todo, {status=reminder,who=joe,text}). 2 | -------------------------------------------------------------------------------- /code/registrar.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(registrar). 3 | -export([start/0, whereis/1, at/2]). 4 | 5 | start() -> register(registrar, spawn(fun() -> loop([]) end)). 6 | 7 | whereis(Thing) -> call({locate, Thing}). 8 | at(Thing, Where) -> cast({isAt, Thing, Where}). 9 | 10 | 11 | 12 | call(Q) -> 13 | registrar ! {self(), Q}, 14 | receive 15 | {registrar, Reply} -> 16 | Reply 17 | end. 18 | 19 | cast(X) -> registrar ! X. 20 | 21 | 22 | 23 | loop(L) -> 24 | receive 25 | {isAt, Thing, Where} -> 26 | L1 = add_location(Thing, Where, L), 27 | loop(L1); 28 | {From, {locate, Thing}} -> 29 | Reply = find_thing(Thing, L), 30 | From ! {registrar, Reply}, 31 | loop(L) 32 | end. 33 | 34 | 35 | 36 | add_location(Thing, At, [{Thing,_}|T]) -> [{Thing, At}|T]; 37 | add_location(Thing, At, [H|T]) -> [H|add_location(Thing, At, T)]; 38 | add_location(Thing, At, []) -> [{Thing, At}]. 39 | 40 | find_thing(Thing, [{Thing, At}|_]) -> {found, At}; 41 | find_thing(Thing, [_|T]) -> find_thing(Thing, T); 42 | find_thing(_Thing, []) -> not_found. 43 | 44 | -------------------------------------------------------------------------------- /code/runtests: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "" >results 3 | for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16\ 4 | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 5 | do 6 | echo $i 7 | erl -boot start_clean -noshell -smp +S $i \ 8 | -s ptests tests $i >> results 9 | done 10 | 11 | 12 | -------------------------------------------------------------------------------- /code/scavenge_urls.erl: -------------------------------------------------------------------------------- 1 | %START:main 2 | -module(scavenge_urls). 3 | -export([urls2htmlFile/2, bin2urls/1]). 4 | -import(lists, [reverse/1, reverse/2, map/2]). 5 | 6 | 7 | 8 | urls2htmlFile(Urls, File) -> 9 | file:write_file(File, urls2html(Urls)). 10 | 11 | bin2urls(Bin) -> gather_urls(binary_to_list(Bin), []). 12 | 13 | urls2html(Urls) -> [h1("Urls"),make_list(Urls)]. 14 | 15 | h1(Title) -> ["

", Title, "

\n"]. 16 | 17 | make_list(L) -> 18 | ["
    \n", 19 | map(fun(I) -> ["
  • ",I,"
  • \n"] end, L), 20 | "
\n"]. 21 | 22 | 23 | 24 | gather_urls(" 25 | {Url, T1} = collect_url_body(T, reverse(" 28 | gather_urls(T, L); 29 | gather_urls([], L) -> 30 | L. 31 | 32 | collect_url_body("" ++ T, L) -> {reverse(L, ""), T}; 33 | collect_url_body([H|T], L) -> collect_url_body(T, [H|L]); 34 | collect_url_body([], _) -> {[],[]}. 35 | 36 | -------------------------------------------------------------------------------- /code/sellaprime.app: -------------------------------------------------------------------------------- 1 | %% This is the application resource file (.app file) for the 'base' 2 | %% application. 3 | {application, sellaprime, 4 | [{description, "The Prime Number Shop"}, 5 | {vsn, "1.0"}, 6 | {modules, [sellaprime_app, sellaprime_supervisor, area_server, 7 | prime_server, lib_lin, lib_primes, my_alarm_handler]}, 8 | {registered,[area_server, prime_server, sellaprime_super]}, 9 | {applications, [kernel,stdlib]}, 10 | {mod, {sellaprime_app,[]}}, 11 | {start_phases, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /code/sellaprime_app.erl: -------------------------------------------------------------------------------- 1 | -module(sellaprime_app). 2 | -behaviour(application). 3 | -export([start/2, stop/1]). 4 | 5 | %%-------------------------------------------------------------------- 6 | %% Function: start(Type, StartArgs) -> {ok, Pid} | 7 | %% {ok, Pid, State} | 8 | %% {error, Reason} 9 | %% Description: This function is called whenever an application 10 | %% is started using application:start/1,2, and should start the processes 11 | %% of the application. If the application is structured according to the 12 | %% OTP design principles as a supervision tree, this means starting the 13 | %% top supervisor of the tree. 14 | %%-------------------------------------------------------------------- 15 | 16 | start(_Type, StartArgs) -> 17 | sellaprime_supervisor:start_link(StartArgs). 18 | 19 | %%-------------------------------------------------------------------- 20 | %% Function: stop(State) -> void() 21 | %% Description: This function is called whenever an application 22 | %% has stopped. It is intended to be the opposite of Module:start/2 and 23 | %% should do any necessary cleaning up. The return value is ignored. 24 | %%-------------------------------------------------------------------- 25 | 26 | stop(_State) -> 27 | ok. 28 | -------------------------------------------------------------------------------- /code/sellaprime_supervisor.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(sellaprime_supervisor). 3 | -behaviour(supervisor). % see erl -man supervisor 4 | 5 | -export([start/0, start_in_shell_for_testing/0, start_link/1, init/1]). 6 | 7 | start() -> 8 | spawn(fun() -> 9 | supervisor:start_link({local,?MODULE}, ?MODULE, _Arg = []) 10 | end). 11 | 12 | start_in_shell_for_testing() -> 13 | {ok, Pid} = supervisor:start_link({local,?MODULE}, ?MODULE, _Arg = []), 14 | unlink(Pid). 15 | 16 | start_link(Args) -> 17 | supervisor:start_link({local,?MODULE}, ?MODULE, Args). 18 | 19 | init([]) -> 20 | %% Install my personal error handler 21 | gen_event:swap_handler(alarm_handler, 22 | {alarm_handler, swap}, 23 | {my_alarm_handler, xyz}), 24 | 25 | {ok, {{one_for_one, 3, 10}, 26 | [{tag1, 27 | {area_server, start_link, []}, 28 | permanent, 29 | 10000, 30 | worker, 31 | [area_server]}, 32 | {tag2, 33 | {prime_server, start_link, []}, 34 | permanent, 35 | 10000, 36 | worker, 37 | [prime_server]} 38 | ]}}. 39 | 40 | 41 | 42 | %% When the supervisor is started, it calls init(Arg). 43 | %% This function should return {ok, {SupFlags, Children}}. 44 | %% 45 | %% SupFlags : {supervision_strategy(), maxR(), maxT()} 46 | %% supervision_strategy() : one_for_one | one_for_all | simple_one_for_one 47 | %% 48 | %% Children : [ChildStartSpecification] 49 | %% ChildStartSpecification : {internal_name(), 50 | %% {module(), function(), args()}, 51 | %% shutdown_time(), 52 | %% child_type(), 53 | %% modules()} 54 | %% See erl -man supervisor for more details. 55 | %% 56 | %% A word on MaxR/MaxT: 57 | %% Choosing a good restart frequency is difficult. The following 58 | %% reasoning might help: 59 | %% 60 | %% - MaxR should be low enough that escalation is not needlessly delayed. 61 | %% Remember that if you have multiple levels of supervisors, MaxR will 62 | %% multiply; you might want to set MaxR=0 for most higher supervisors. 63 | %% - MaxT should be low enough that unrelated restarts aren't counted as 64 | %% looping restart (think: how long does it take for the effects of a 65 | %% problem to "heal"?); if MaxT is too low, there may not be time enough 66 | %% for MaxR restarts. 67 | %% 68 | %% In general, think about what should happen if a certain process restars. 69 | %% Some processes may be designed as "kernel processes", such that the only 70 | %% reasonable course of action, should they crash, is to terminate the node. 71 | %% 72 | %% I've chosen three restarts in 10 seconds. 73 | %% 74 | -------------------------------------------------------------------------------- /code/server1.erl: -------------------------------------------------------------------------------- 1 | -module(server1). 2 | -export([start/2, rpc/2]). 3 | 4 | start(Name, Mod) -> 5 | register(Name, spawn(fun() -> loop(Name, Mod, Mod:init()) end)). 6 | 7 | rpc(Name, Request) -> 8 | Name ! {self(), Request}, 9 | receive 10 | {Name, Response} -> Response 11 | end. 12 | 13 | loop(Name, Mod, State) -> 14 | receive 15 | {From, Request} -> 16 | {Response, State1} = Mod:handle(Request, State), 17 | From ! {Name, Response}, 18 | loop(Name, Mod, State1) 19 | end. 20 | -------------------------------------------------------------------------------- /code/server2.erl: -------------------------------------------------------------------------------- 1 | -module(server2). 2 | -export([start/2, rpc/2]). 3 | 4 | start(Name, Mod) -> 5 | register(Name, spawn(fun() -> loop(Name,Mod,Mod:init()) end)). 6 | 7 | rpc(Name, Request) -> 8 | Name ! {self(), Request}, 9 | receive 10 | {Name, crash} -> exit(rpc); 11 | {Name, ok, Response} -> Response 12 | end. 13 | 14 | loop(Name, Mod, OldState) -> 15 | receive 16 | {From, Request} -> 17 | try Mod:handle(Request, OldState) of 18 | {Response, NewState} -> 19 | From ! {Name, ok, Response}, 20 | loop(Name, Mod, NewState) 21 | catch 22 | _:Why -> 23 | log_the_error(Name, Request, Why), 24 | %% send a message to cause the client to crash 25 | From ! {Name, crash}, 26 | %% loop with the *original* state 27 | loop(Name, Mod, OldState) 28 | end 29 | end. 30 | 31 | log_the_error(Name, Request, Why) -> 32 | io:format("Server ~p request ~p ~n" 33 | "caused exception ~p~n", 34 | [Name, Request, Why]). 35 | -------------------------------------------------------------------------------- /code/server3.erl: -------------------------------------------------------------------------------- 1 | -module(server3). 2 | -export([start/2, rpc/2, swap_code/2]). 3 | 4 | start(Name, Mod) -> 5 | register(Name, 6 | spawn(fun() -> loop(Name,Mod,Mod:init()) end)). 7 | 8 | swap_code(Name, Mod) -> rpc(Name, {swap_code, Mod}). 9 | 10 | rpc(Name, Request) -> 11 | Name ! {self(), Request}, 12 | receive 13 | {Name, Response} -> Response 14 | end. 15 | 16 | loop(Name, Mod, OldState) -> 17 | receive 18 | {From, {swap_code, NewCallBackMod}} -> 19 | From ! {Name, ack}, 20 | loop(Name, NewCallBackMod, OldState); 21 | {From, Request} -> 22 | {Response, NewState} = Mod:handle(Request, OldState), 23 | From ! {Name, Response}, 24 | loop(Name, Mod, NewState) 25 | end. 26 | -------------------------------------------------------------------------------- /code/server4.erl: -------------------------------------------------------------------------------- 1 | -module(server4). 2 | -export([start/2, rpc/2, swap_code/2]). 3 | 4 | start(Name, Mod) -> 5 | register(Name, spawn(fun() -> loop(Name,Mod,Mod:init()) end)). 6 | 7 | swap_code(Name, Mod) -> rpc(Name, {swap_code, Mod}). 8 | 9 | rpc(Name, Request) -> 10 | Name ! {self(), Request}, 11 | receive 12 | {Name, crash} -> exit(rpc); 13 | {Name, ok, Response} -> Response 14 | end. 15 | 16 | loop(Name, Mod, OldState) -> 17 | receive 18 | {From, {swap_code, NewCallbackMod}} -> 19 | From ! {Name, ok, ack}, 20 | loop(Name, NewCallbackMod, OldState); 21 | {From, Request} -> 22 | try Mod:handle(Request, OldState) of 23 | {Response, NewState} -> 24 | From ! {Name, ok, Response}, 25 | loop(Name, Mod, NewState) 26 | catch 27 | _: Why -> 28 | log_the_error(Name, Request, Why), 29 | From ! {Name, crash}, 30 | loop(Name, Mod, OldState) 31 | end 32 | end. 33 | 34 | log_the_error(Name, Request, Why) -> 35 | io:format("Server ~p request ~p ~n" 36 | "caused exception ~p~n", 37 | [Name, Request, Why]). 38 | -------------------------------------------------------------------------------- /code/server5.erl: -------------------------------------------------------------------------------- 1 | -module(server5). 2 | -export([start/0, rpc/2]). 3 | 4 | start() -> spawn(fun() -> wait() end). 5 | 6 | wait() -> 7 | receive 8 | {become, F} -> F() 9 | end. 10 | 11 | rpc(Pid, Q) -> 12 | Pid ! {self(), Q}, 13 | receive 14 | {Pid, Reply} -> Reply 15 | end. 16 | -------------------------------------------------------------------------------- /code/shop.erl: -------------------------------------------------------------------------------- 1 | -module(shop). 2 | -export([cost/1]). 3 | 4 | 5 | cost(oranges) -> 5; 6 | cost(newspaper) -> 8; 7 | cost(apples) -> 2; 8 | cost(pears) -> 9; 9 | cost(milk) -> 7. 10 | %END:function 11 | -------------------------------------------------------------------------------- /code/shop1.erl: -------------------------------------------------------------------------------- 1 | -module(shop1). 2 | -export([total/1]). 3 | 4 | total([{What, N}|T]) -> shop:cost(What) * N + total(T); 5 | total([]) -> 0. 6 | -------------------------------------------------------------------------------- /code/shop2.erl: -------------------------------------------------------------------------------- 1 | -module(shop2). 2 | -export([total/1]). 3 | -import(lists, [map/2, sum/1]). 4 | 5 | total(L) -> 6 | sum(map(fun({What, N}) -> shop:cost(What) * N end, L)). 7 | 8 | -------------------------------------------------------------------------------- /code/shop3.erl: -------------------------------------------------------------------------------- 1 | -module(shop3). 2 | -export([total/1]). 3 | 4 | total([{What, N}|T]) -> 5 | case (catch shop:cost(What)) of 6 | {'EXIT', _} -> 7 | io:format("The shop does not sell ~p~n",[What]), 8 | total(T); 9 | Cost -> 10 | Cost * N + total(T) 11 | end; 12 | total([]) -> 13 | 0. 14 | 15 | -------------------------------------------------------------------------------- /code/simple.app: -------------------------------------------------------------------------------- 1 | {application, 'simple', 2 | [{description, "A simple application"}, 3 | {vsn, "1.0"}, 4 | {modules, [simple,kv,packet_assembler, 5 | simple_sup,simple_logger]}, 6 | {maxT, infinity}, 7 | {registered, [kv, my_simple_event_logger, 8 | my_simple_packet_assembler]}, 9 | {applications, []}, 10 | {included_applications, []}, 11 | {env, []}, 12 | {mod, {simple, go}}]}. 13 | 14 | -------------------------------------------------------------------------------- /code/socket_dist/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .erl .beam .yrl 2 | 3 | .erl.beam: 4 | erlc -W $< 5 | 6 | ## to test lib_chan 7 | ## make server in one window 8 | ## make client in the other window 9 | 10 | MODS = any_apply \ 11 | chat_client chat_group chat_server\ 12 | io_widget kvs\ 13 | lib_chan lib_chan_mm lib_chan_cs lib_chan_test\ 14 | lib_chan_auth \ 15 | mod_echo mod_math mod_chat_controller mod_name_server\ 16 | test1 test_name_server mod_srpc 17 | 18 | 19 | ERL = erl -boot start_clean 20 | 21 | all: compile 22 | @echo "To run the chat test program" 23 | @echo " 1) start two xterms" 24 | @echo " 2) in xterm one type -- make chat_server" 25 | @echo " 3) in xterm two type -- make chat_client" 26 | @echo " Note: remember to type make in the directory above this" 27 | @echo " before running make in this directory" 28 | @echo "To run the lib_chan test program" 29 | @echo " 1) start two xterms" 30 | @echo " 2) in xterm one type -- make server" 31 | @echo " 3) in xterm two type -- make client" 32 | @echo " Ignore any output in the server window" 33 | @echo " The results of the test are printed in the client window" 34 | 35 | compile: ${MODS:%=%.beam} 36 | mkdir -p ${HOME}/.erlang_config/ 37 | cp conf ${HOME}/.erlang_config/lib_chan.conf 38 | @echo "make clean - clean up" 39 | 40 | chat_client: compile 41 | erl -pa ../ -s chat_client test 42 | 43 | test: compile 44 | erl -s chat_tests start 45 | 46 | chat_server: compile 47 | erl -pa ../ -s chat_server start 48 | 49 | server: compile 50 | erl -pa ../ -boot start_clean -pa '.' -s lib_chan_test start_server 51 | 52 | client: compile 53 | erl -pa ../ -boot start_clean -pa '.' -s lib_chan_test start_client 54 | 55 | clean: 56 | rm -rf *.beam erl_crash.dump 57 | -------------------------------------------------------------------------------- /code/socket_dist/any_apply.erl: -------------------------------------------------------------------------------- 1 | -module(any_apply). 2 | 3 | -export([mfa/3]). 4 | 5 | mfa(Mod, Func, Args) -> 6 | apply(Mod, Func, Args). 7 | -------------------------------------------------------------------------------- /code/socket_dist/chat.conf: -------------------------------------------------------------------------------- 1 | {port, 2223}. 2 | {service, chat, password,"AsDT67aQ",mfa,mod_chat_controller,start,[]}. 3 | 4 | -------------------------------------------------------------------------------- /code/socket_dist/chat_client.erl: -------------------------------------------------------------------------------- 1 | -module(chat_client). 2 | 3 | -import(io_widget, 4 | [get_state/1, insert_str/2, set_prompt/2, set_state/2, 5 | set_title/2, set_handler/2, update_state/3]). 6 | 7 | -export([start/0, test/0, connect/5]). 8 | 9 | 10 | start() -> 11 | connect("localhost", 2223, "AsDT67aQ", "general", "joe"). 12 | 13 | 14 | test() -> 15 | connect("localhost", 2223, "AsDT67aQ", "general", "joe"), 16 | connect("localhost", 2223, "AsDT67aQ", "general", "jane"), 17 | connect("localhost", 2223, "AsDT67aQ", "general", "jim"), 18 | connect("localhost", 2223, "AsDT67aQ", "general", "sue"). 19 | 20 | 21 | connect(Host, Port, HostPsw, Group, Nick) -> 22 | spawn(fun() -> handler(Host, Port, HostPsw, Group, Nick) end). 23 | 24 | handler(Host, Port, HostPsw, Group, Nick) -> 25 | process_flag(trap_exit, true), 26 | Widget = io_widget:start(self()), 27 | set_title(Widget, Nick), 28 | set_state(Widget, Nick), 29 | set_prompt(Widget, [Nick, " > "]), 30 | set_handler(Widget, fun parse_command/1), 31 | start_connector(Host, Port, HostPsw), 32 | disconnected(Widget, Group, Nick). 33 | 34 | 35 | 36 | disconnected(Widget, Group, Nick) -> 37 | receive 38 | {connected, MM} -> 39 | insert_str(Widget, "connected to server\nsending data\n"), 40 | lib_chan_mm:send(MM, {login, Group, Nick}), 41 | wait_login_response(Widget, MM); 42 | {Widget, destroyed} -> 43 | exit(died); 44 | {status, S} -> 45 | insert_str(Widget, to_str(S)), 46 | disconnected(Widget, Group, Nick); 47 | Other -> 48 | io:format("chat_client disconnected unexpected:~p~n",[Other]), 49 | disconnected(Widget, Group, Nick) 50 | end. 51 | 52 | 53 | 54 | wait_login_response(Widget, MM) -> 55 | receive 56 | {chan, MM, ack} -> 57 | active(Widget, MM); 58 | Other -> 59 | io:format("chat_client login unexpected:~p~n",[Other]), 60 | wait_login_response(Widget, MM) 61 | end. 62 | 63 | 64 | 65 | active(Widget, MM) -> 66 | receive 67 | {Widget, Nick, Str} -> 68 | lib_chan_mm:send(MM, {relay, Nick, Str}), 69 | active(Widget, MM); 70 | {chan, MM, {msg, From, Pid, Str}} -> 71 | insert_str(Widget, [From,"@",pid_to_list(Pid)," ", Str, "\n"]), 72 | active(Widget, MM); 73 | {'EXIT',Widget,windowDestroyed} -> 74 | lib_chan_mm:close(MM); 75 | {close, MM} -> 76 | exit(serverDied); 77 | Other -> 78 | io:format("chat_client active unexpected:~p~n",[Other]), 79 | active(Widget, MM) 80 | end. 81 | 82 | 83 | 84 | start_connector(Host, Port, Pwd) -> 85 | S = self(), 86 | spawn_link(fun() -> try_to_connect(S, Host, Port, Pwd) end). 87 | 88 | try_to_connect(Parent, Host, Port, Pwd) -> 89 | %% Parent is the Pid of the process that spawned this process 90 | case lib_chan:connect(Host, Port, chat, Pwd, []) of 91 | {error, _Why} -> 92 | Parent ! {status, {cannot, connect, Host, Port}}, 93 | sleep(2000), 94 | try_to_connect(Parent, Host, Port, Pwd); 95 | {ok, MM} -> 96 | lib_chan_mm:controller(MM, Parent), 97 | Parent ! {connected, MM}, 98 | exit(connectorFinished) 99 | end. 100 | 101 | 102 | sleep(T) -> 103 | receive 104 | after T -> true 105 | end. 106 | 107 | to_str(Term) -> 108 | io_lib:format("~p~n",[Term]). 109 | 110 | parse_command(Str) -> skip_to_gt(Str). 111 | 112 | skip_to_gt(">" ++ T) -> T; 113 | skip_to_gt([_|T]) -> skip_to_gt(T); 114 | skip_to_gt([]) -> exit("no >"). 115 | -------------------------------------------------------------------------------- /code/socket_dist/chat_group.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(chat_group). 3 | -import(lib_chan_mm, [send/2, controller/2]). 4 | -import(lists, [foreach/2, reverse/2]). 5 | 6 | -export([start/2]). 7 | 8 | start(C, Nick) -> 9 | process_flag(trap_exit, true), 10 | controller(C, self()), 11 | send(C, ack), 12 | self() ! {chan, C, {relay, Nick, "I'm starting the group"}}, 13 | group_controller([{C,Nick}]). 14 | 15 | 16 | 17 | delete(Pid, [{Pid,Nick}|T], L) -> {Nick, reverse(T, L)}; 18 | delete(Pid, [H|T], L) -> delete(Pid, T, [H|L]); 19 | delete(_, [], L) -> {"????", L}. 20 | 21 | 22 | 23 | group_controller([]) -> 24 | exit(allGone); 25 | group_controller(L) -> 26 | receive 27 | {chan, C, {relay, Nick, Str}} -> 28 | foreach(fun({Pid,_}) -> send(Pid, {msg,Nick,C,Str}) end, L), 29 | group_controller(L); 30 | {login, C, Nick} -> 31 | controller(C, self()), 32 | send(C, ack), 33 | self() ! {chan, C, {relay, Nick, "I'm joining the group"}}, 34 | group_controller([{C,Nick}|L]); 35 | {chan_closed, C} -> 36 | {Nick, L1} = delete(C, L, []), 37 | self() ! {chan, C, {relay, Nick, "I'm leaving the group"}}, 38 | group_controller(L1); 39 | Any -> 40 | io:format("group controller received Msg=~p~n", [Any]), 41 | group_controller(L) 42 | end. 43 | 44 | -------------------------------------------------------------------------------- /code/socket_dist/chat_server.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(chat_server). 3 | -import(lib_chan_mm, [send/2, controller/2]). 4 | -import(lists, [delete/2, foreach/2, map/2, member/2,reverse/2]). 5 | 6 | -compile(export_all). 7 | 8 | 9 | start() -> 10 | start_server(), 11 | lib_chan:start_server("chat.conf"). 12 | 13 | start_server() -> 14 | register(chat_server, 15 | spawn(fun() -> 16 | process_flag(trap_exit, true), 17 | Val= (catch server_loop([])), 18 | io:format("Server terminated with:~p~n",[Val]) 19 | end)). 20 | 21 | 22 | 23 | server_loop(L) -> 24 | receive 25 | {mm, Channel, {login, Group, Nick}} -> 26 | case lookup(Group, L) of 27 | {ok, Pid} -> 28 | Pid ! {login, Channel, Nick}, 29 | server_loop(L); 30 | error -> 31 | Pid = spawn_link(fun() -> 32 | chat_group:start(Channel, Nick) 33 | end), 34 | server_loop([{Group,Pid}|L]) 35 | end; 36 | {mm_closed, _} -> 37 | server_loop(L); 38 | {'EXIT', Pid, allGone} -> 39 | L1 = remove_group(Pid, L), 40 | server_loop(L1); 41 | Msg -> 42 | io:format("Server received Msg=~p~n", 43 | [Msg]), 44 | server_loop(L) 45 | end. 46 | 47 | 48 | 49 | lookup(G, [{G,Pid}|_]) -> {ok, Pid}; 50 | lookup(G, [_|T]) -> lookup(G, T); 51 | lookup(_,[]) -> error. 52 | 53 | remove_group(Pid, [{G,Pid}|T]) -> io:format("~p removed~n",[G]), T; 54 | remove_group(Pid, [H|T]) -> [H|remove_group(Pid, T)]; 55 | remove_group(_, []) -> []. 56 | 57 | -------------------------------------------------------------------------------- /code/socket_dist/conf: -------------------------------------------------------------------------------- 1 | {port, 1234}. 2 | {service, nameServer, password, "ABXy45", mfa, mod_name_server, start_me_up, notUsed}. 3 | {service, srpc, password, "secret", mfa, mod_srpc, start, [test1]}. 4 | {service, root, password, "verySecret", mfa, mod_srpc, start, [any_apply]}. 5 | {service, test1, password, "test1", mfa, mod_test1, start, {hi, joe}}. 6 | {service, echo, password, "echo1", mfa, mod_echo, start, {server,args}}. 7 | {service, math, password, "qwerty", mfa, mod_math, run, []}. 8 | -------------------------------------------------------------------------------- /code/socket_dist/config1: -------------------------------------------------------------------------------- 1 | {port, 2233}. 2 | {service, math, password, "qwerty", mfa, mod_math, run, []}. 3 | -------------------------------------------------------------------------------- /code/socket_dist/io_widget.erl: -------------------------------------------------------------------------------- 1 | -module(io_widget). 2 | 3 | -export([get_state/1, 4 | start/1, test/0, 5 | set_handler/2, 6 | set_prompt/2, 7 | set_state/2, 8 | set_title/2, insert_str/2, update_state/3]). 9 | 10 | start(Pid) -> 11 | gs:start(), 12 | spawn_link(fun() -> widget(Pid) end). 13 | 14 | get_state(Pid) -> rpc(Pid, get_state). 15 | set_title(Pid, Str) -> Pid ! {title, Str}. 16 | set_handler(Pid, Fun) -> Pid ! {handler, Fun}. 17 | set_prompt(Pid, Str) -> Pid ! {prompt, Str}. 18 | set_state(Pid, State) -> Pid ! {state, State}. 19 | insert_str(Pid, Str) -> Pid ! {insert, Str}. 20 | update_state(Pid, N, X) -> Pid ! {updateState, N, X}. 21 | 22 | rpc(Pid, Q) -> 23 | Pid ! {self(), Q}, 24 | receive 25 | {Pid, R} -> 26 | R 27 | end. 28 | 29 | widget(Pid) -> 30 | Size = [{width,500},{height,200}], 31 | Win = gs:window(gs:start(), 32 | [{map,true},{configure,true},{title,"window"}|Size]), 33 | gs:frame(packer, Win,[{packer_x, [{stretch,1,500}]}, 34 | {packer_y, [{stretch,10,100,120}, 35 | {stretch,1,15,15}]}]), 36 | gs:create(editor,editor,packer, [{pack_x,1},{pack_y,1},{vscroll,right}]), 37 | gs:create(entry, entry, packer, [{pack_x,1},{pack_y,2},{keypress,true}]), 38 | gs:config(packer, Size), 39 | Prompt = " > ", 40 | State = nil, 41 | gs:config(entry, {insert,{0,Prompt}}), 42 | loop(Win, Pid, Prompt, State, fun parse/1). 43 | 44 | loop(Win, Pid, Prompt, State, Parse) -> 45 | receive 46 | {From, get_state} -> 47 | From ! {self(), State}, 48 | loop(Win, Pid, Prompt, State, Parse); 49 | {handler, Fun} -> 50 | loop(Win, Pid, Prompt, State, Fun); 51 | {prompt, Str} -> 52 | %% this clobbers the line being input ... 53 | %% this could be fixed - hint 54 | gs:config(entry, {delete,{0,last}}), 55 | gs:config(entry, {insert,{0,Str}}), 56 | loop(Win, Pid, Str, State, Parse); 57 | {state, S} -> 58 | loop(Win, Pid, Prompt, S, Parse); 59 | {title, Str} -> 60 | gs:config(Win, [{title, Str}]), 61 | loop(Win, Pid, Prompt, State, Parse); 62 | {insert, Str} -> 63 | gs:config(editor, {insert,{'end',Str}}), 64 | scroll_to_show_last_line(), 65 | loop(Win, Pid, Prompt, State, Parse); 66 | {updateState, N, X} -> 67 | io:format("setelemtn N=~p X=~p State=~p~n",[N,X,State]), 68 | State1 = setelement(N, State, X), 69 | loop(Win, Pid, Prompt, State1, Parse); 70 | {gs,_,destroy,_,_} -> 71 | io:format("Destroyed~n",[]), 72 | exit(windowDestroyed); 73 | {gs, entry,keypress,_,['Return'|_]} -> 74 | Text = gs:read(entry, text), 75 | %% io:format("Read:~p~n",[Text]), 76 | gs:config(entry, {delete,{0,last}}), 77 | gs:config(entry, {insert,{0,Prompt}}), 78 | try Parse(Text) of 79 | Term -> 80 | Pid ! {self(), State, Term} 81 | catch 82 | _:_ -> 83 | self() ! {insert, "** bad input**\n** /h for help\n"} 84 | end, 85 | loop(Win, Pid, Prompt, State, Parse); 86 | {gs,_,configure,[],[W,H,_,_]} -> 87 | gs:config(packer, [{width,W},{height,H}]), 88 | loop(Win, Pid, Prompt, State, Parse); 89 | {gs, entry,keypress,_,_} -> 90 | loop(Win, Pid, Prompt, State, Parse); 91 | Any -> 92 | io:format("Discarded:~p~n",[Any]), 93 | loop(Win, Pid, Prompt, State, Parse) 94 | end. 95 | 96 | scroll_to_show_last_line() -> 97 | Size = gs:read(editor, size), 98 | Height = gs:read(editor, height), 99 | CharHeight = gs:read(editor, char_height), 100 | TopRow = Size - Height/CharHeight, 101 | if TopRow > 0 -> gs:config(editor, {vscrollpos, TopRow}); 102 | true -> gs:config(editor, {vscrollpos, 0}) 103 | end. 104 | 105 | test() -> 106 | spawn(fun() -> test1() end). 107 | 108 | test1() -> 109 | W = io_widget:start(self()), 110 | io_widget:set_title(W, "Test window"), 111 | loop(W). 112 | 113 | loop(W) -> 114 | receive 115 | {W, {str, Str}} -> 116 | Str1 = Str ++ "\n", 117 | io_widget:insert_str(W, Str1), 118 | loop(W) 119 | end. 120 | 121 | parse(Str) -> 122 | {str, Str}. 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /code/socket_dist/kvs.erl: -------------------------------------------------------------------------------- 1 | -module(kvs). 2 | -export([start/0, store/2, lookup/1]). 3 | 4 | start() -> register(kvs, spawn(fun() -> loop() end)). 5 | 6 | store(Key, Value) -> rpc({store, Key, Value}). 7 | 8 | lookup(Key) -> rpc({lookup, Key}). 9 | 10 | rpc(Q) -> 11 | kvs ! {self(), Q}, 12 | receive 13 | {kvs, Reply} -> 14 | Reply 15 | end. 16 | 17 | loop() -> 18 | receive 19 | {From, {store, Key, Value}} -> 20 | put(Key, {ok, Value}), 21 | From ! {kvs, true}, 22 | loop(); 23 | {From, {lookup, Key}} -> 24 | From ! {kvs, get(Key)}, 25 | loop() 26 | end. 27 | -------------------------------------------------------------------------------- /code/socket_dist/lib_chan_auth.erl: -------------------------------------------------------------------------------- 1 | -module(lib_chan_auth). 2 | 3 | -export([make_challenge/0, make_response/2, is_response_correct/3]). 4 | 5 | make_challenge() -> 6 | random_string(25). 7 | 8 | make_response(Challenge, Secret) -> 9 | lib_md5:string(Challenge ++ Secret). 10 | 11 | is_response_correct(Challenge, Response, Secret) -> 12 | case lib_md5:string(Challenge ++ Secret) of 13 | Response -> true; 14 | _ -> false 15 | end. 16 | 17 | %% random_string(N) -> a random string with N characters. 18 | 19 | random_string(N) -> random_seed(), random_string(N, []). 20 | 21 | random_string(0, D) -> D; 22 | random_string(N, D) -> 23 | random_string(N-1, [random:uniform(26)-1+$a|D]). 24 | 25 | random_seed() -> 26 | {_,_,X} = erlang:now(), 27 | {H,M,S} = time(), 28 | H1 = H * X rem 32767, 29 | M1 = M * X rem 32767, 30 | S1 = S * X rem 32767, 31 | put(random_seed, {H1,M1,S1}). 32 | -------------------------------------------------------------------------------- /code/socket_dist/lib_chan_cs.erl: -------------------------------------------------------------------------------- 1 | -module(lib_chan_cs). 2 | %% cs stands for client_server 3 | 4 | -export([start_raw_server/4, start_raw_client/3]). 5 | -export([stop/1]). 6 | -export([children/1]). 7 | 8 | 9 | %% start_raw_server(Port, Fun, Max, PacketLength) 10 | %% This server accepts up to Max connections on Port 11 | %% The *first* time a connection is made to Port 12 | %% Then Fun(Socket) is called. 13 | %% Thereafter messages to the socket result in messages to the handler. 14 | %% PacketLength is usually 0,1,2 or 4 (see the inet manual page for details). 15 | 16 | %% tcp_is typically used as follows: 17 | %% To setup a listener 18 | %% start_agent(Port) -> 19 | %% process_flag(trap_exit, true), 20 | %% lib_chan_server:start_raw_server(Port, 21 | %% fun(Socket) -> input_handler(Socket) end, 22 | %% 15, 0). 23 | 24 | start_raw_client(Host, Port, PacketLength) -> 25 | gen_tcp:connect(Host, Port, 26 | [binary, {active, true}, {packet, PacketLength}]). 27 | 28 | %% Note when start_raw_server returns it should be ready to 29 | %% Immediately accept connections 30 | 31 | start_raw_server(Port, Fun, Max, PacketLength) -> 32 | Name = port_name(Port), 33 | case whereis(Name) of 34 | undefined -> 35 | Self = self(), 36 | Pid = spawn_link(fun() -> 37 | cold_start(Self,Port,Fun,Max,PacketLength) 38 | end), 39 | receive 40 | {Pid, ok} -> 41 | register(Name, Pid), 42 | {ok, self()}; 43 | {Pid, Error} -> 44 | Error 45 | end; 46 | _Pid -> 47 | {error, already_started} 48 | end. 49 | 50 | stop(Port) when integer(Port) -> 51 | Name = port_name(Port), 52 | case whereis(Name) of 53 | undefined -> 54 | not_started; 55 | Pid -> 56 | exit(Pid, kill), 57 | (catch unregister(Name)), 58 | stopped 59 | end. 60 | children(Port) when integer(Port) -> 61 | port_name(Port) ! {children, self()}, 62 | receive 63 | {session_server, Reply} -> Reply 64 | end. 65 | 66 | 67 | port_name(Port) when integer(Port) -> 68 | list_to_atom("portServer" ++ integer_to_list(Port)). 69 | 70 | 71 | cold_start(Master, Port, Fun, Max, PacketLength) -> 72 | process_flag(trap_exit, true), 73 | %% io:format("Starting a port server on ~p...~n",[Port]), 74 | case gen_tcp:listen(Port, [binary, 75 | %% {dontroute, true}, 76 | {nodelay,true}, 77 | {packet, PacketLength}, 78 | {reuseaddr, true}, 79 | {active, true}]) of 80 | {ok, Listen} -> 81 | %% io:format("Listening to:~p~n",[Listen]), 82 | Master ! {self(), ok}, 83 | New = start_accept(Listen, Fun), 84 | %% Now we're ready to run 85 | socket_loop(Listen, New, [], Fun, Max); 86 | Error -> 87 | Master ! {self(), Error} 88 | end. 89 | 90 | 91 | socket_loop(Listen, New, Active, Fun, Max) -> 92 | receive 93 | {istarted, New} -> 94 | Active1 = [New|Active], 95 | possibly_start_another(false,Listen,Active1,Fun,Max); 96 | {'EXIT', New, _Why} -> 97 | %% io:format("Child exit=~p~n",[Why]), 98 | possibly_start_another(false,Listen,Active,Fun,Max); 99 | {'EXIT', Pid, _Why} -> 100 | %% io:format("Child exit=~p~n",[Why]), 101 | Active1 = lists:delete(Pid, Active), 102 | possibly_start_another(New,Listen,Active1,Fun,Max); 103 | {children, From} -> 104 | From ! {session_server, Active}, 105 | socket_loop(Listen,New,Active,Fun,Max); 106 | _Other -> 107 | socket_loop(Listen,New,Active,Fun,Max) 108 | end. 109 | 110 | 111 | possibly_start_another(New, Listen, Active, Fun, Max) 112 | when pid(New) -> 113 | socket_loop(Listen, New, Active, Fun, Max); 114 | possibly_start_another(false, Listen, Active, Fun, Max) -> 115 | case length(Active) of 116 | N when N < Max -> 117 | New = start_accept(Listen, Fun), 118 | socket_loop(Listen, New, Active, Fun,Max); 119 | _ -> 120 | socket_loop(Listen, false, Active, Fun, Max) 121 | end. 122 | 123 | start_accept(Listen, Fun) -> 124 | S = self(), 125 | spawn_link(fun() -> start_child(S, Listen, Fun) end). 126 | 127 | start_child(Parent, Listen, Fun) -> 128 | case gen_tcp:accept(Listen) of 129 | {ok, Socket} -> 130 | Parent ! {istarted,self()}, % tell the controller 131 | inet:setopts(Socket, [{packet,4}, 132 | binary, 133 | {nodelay,true}, 134 | {active, true}]), 135 | %% before we activate socket 136 | %% io:format("running the child:~p Fun=~p~n", [Socket, Fun]), 137 | process_flag(trap_exit, true), 138 | case (catch Fun(Socket)) of 139 | {'EXIT', normal} -> 140 | true; 141 | {'EXIT', Why} -> 142 | io:format("Port process dies with exit:~p~n",[Why]), 143 | true; 144 | _ -> 145 | %% not an exit so everything's ok 146 | true 147 | end 148 | end. 149 | 150 | 151 | -------------------------------------------------------------------------------- /code/socket_dist/lib_chan_mm.erl: -------------------------------------------------------------------------------- 1 | %% Protocol 2 | %% To the controlling process 3 | %% {chan, MM, Term} 4 | %% {chan_closed, MM} 5 | %% From any process 6 | %% {send, Term} 7 | %% close 8 | 9 | -module(lib_chan_mm). 10 | %% TCP Middle man 11 | %% Models the interface to gen_tcp 12 | 13 | -export([loop/2, send/2, close/1, controller/2, set_trace/2, trace_with_tag/2]). 14 | 15 | send(Pid, Term) -> Pid ! {send, Term}. 16 | close(Pid) -> Pid ! close. 17 | controller(Pid, Pid1) -> Pid ! {setController, Pid1}. 18 | set_trace(Pid, X) -> Pid ! {trace, X}. 19 | 20 | trace_with_tag(Pid, Tag) -> 21 | set_trace(Pid, {true, 22 | fun(Msg) -> 23 | io:format("MM:~p ~p~n",[Tag, Msg]) 24 | end}). 25 | 26 | loop(Socket, Pid) -> 27 | %% trace_with_tag(self(), trace), 28 | process_flag(trap_exit, true), 29 | loop1(Socket, Pid, false). 30 | 31 | loop1(Socket, Pid, Trace) -> 32 | receive 33 | {tcp, Socket, Bin} -> 34 | Term = binary_to_term(Bin), 35 | trace_it(Trace,{socketReceived, Term}), 36 | Pid ! {chan, self(), Term}, 37 | loop1(Socket, Pid, Trace); 38 | {tcp_closed, Socket} -> 39 | trace_it(Trace, socketClosed), 40 | Pid ! {chan_closed, self()}; 41 | {'EXIT', Pid, Why} -> 42 | trace_it(Trace,{controllingProcessExit, Why}), 43 | gen_tcp:close(Socket); 44 | {setController, Pid1} -> 45 | trace_it(Trace, {changedController, Pid}), 46 | loop1(Socket, Pid1, Trace); 47 | {trace, Trace1} -> 48 | trace_it(Trace, {setTrace, Trace1}), 49 | loop1(Socket, Pid, Trace1); 50 | close -> 51 | trace_it(Trace, closedByClient), 52 | gen_tcp:close(Socket); 53 | {send, Term} -> 54 | trace_it(Trace, {sendingMessage, Term}), 55 | gen_tcp:send(Socket, term_to_binary(Term)), 56 | loop1(Socket, Pid, Trace); 57 | UUg -> 58 | io:format("lib_chan_mm: protocol error:~p~n",[UUg]), 59 | loop1(Socket, Pid, Trace) 60 | end. 61 | trace_it(false, _) -> void; 62 | trace_it({true, F}, M) -> F(M). 63 | -------------------------------------------------------------------------------- /code/socket_dist/lib_chan_test.erl: -------------------------------------------------------------------------------- 1 | -module(lib_chan_test). 2 | 3 | -compile(export_all). 4 | 5 | -import(lib_chan, [connect/5, rpc/4, disconnect/1]). 6 | 7 | start_server() -> 8 | lib_chan:start_server(), 9 | wait(). 10 | 11 | start_client() -> 12 | io:format("Running client tests~n"), 13 | tests(1). 14 | 15 | test(1) -> 16 | {ok, MM} = connect("localhost", 1234, echo, "echo1", {echo,client,arg1}), 17 | MM ! {send, "hello"}, 18 | receive 19 | {chan, MM, "hello"} -> 20 | io:format("test(1) succeeded~n"); 21 | Other -> 22 | io:format("test(1) failed:~p~n", [Other]) 23 | end, 24 | exit(MM, close); 25 | test(2) -> 26 | {ok, MM} = connect("localhost", 1234, math, "qwerty", ""), 27 | MM ! {send, {factorial, 6}}, 28 | receive 29 | {chan, MM, 720} -> 30 | io:format("test(2) succeeded~n"); 31 | Other -> 32 | io:format("test(2) failed:~p~n", [Other]) 33 | end, 34 | MM ! close; 35 | test(3) -> 36 | case connect("localhost", 1234, mod_non_exists, "facSecret", "") of 37 | {error, badService} -> 38 | io:format("test(3) succeeded~n"); 39 | Other -> 40 | io:format("test(3) failed:~p~n", [Other]) 41 | end; 42 | test(4) -> 43 | case connect("localhost", 1234, math, "badSecret", "") of 44 | {error, authFail} -> 45 | io:format("test(4) succeeded~n"); 46 | Other -> 47 | io:format("test(4) failed:~p~n",[Other]) 48 | end; 49 | test(5) -> 50 | {ok, MM} = connect("localhost", 1234, srpc, "secret", ""), 51 | case mod_srpc:rpc(MM, test1, fac, [20]) of 52 | 2432902008176640000 -> 53 | io:format("test(5) succeeded~n"); 54 | Other -> 55 | io:format("test(5) failed:~p~n",[Other]) 56 | end, 57 | disconnect(MM); 58 | test(6) -> 59 | {ok, MM} = connect("localhost", 1234, srpc, "secret", ""), 60 | case (catch mod_srpc:rpc(MM, test2, fac, [20])) of 61 | {'EXIT', {modNotAllowed, test2}} -> 62 | io:format("test(6) succeeded~n"); 63 | Other -> 64 | io:format("test(6) failed:~p~n",[Other]) 65 | end, 66 | disconnect(MM); 67 | test(7) -> 68 | {ok, MM} = connect("localhost", 1234, root, "verySecret", ""), 69 | case (catch mod_srpc:rpc(MM, any_apply, mfa, [test1, fac, [20]])) of 70 | 2432902008176640000 -> 71 | io:format("test(7) succeeded~n"); 72 | Other -> 73 | io:format("test(7) failed:~p~n",[Other]) 74 | end; 75 | test(8) -> 76 | done. 77 | 78 | fac(0) -> 1; 79 | fac(N) -> N * fac(N-1). 80 | 81 | tests(N) -> 82 | case test(N) of 83 | done -> init:stop(); 84 | _Val -> tests(N+1) 85 | end. 86 | 87 | wait() -> 88 | receive 89 | _Any -> 90 | wait() 91 | end. 92 | 93 | -------------------------------------------------------------------------------- /code/socket_dist/mod_chat_controller.erl: -------------------------------------------------------------------------------- 1 | -module(mod_chat_controller). 2 | -export([start/3]). 3 | -import(lib_chan_mm, [send/2]). 4 | 5 | start(MM, _, _) -> 6 | process_flag(trap_exit, true), 7 | io:format("mod_chat_controller off we go ...~p~n",[MM]), 8 | loop(MM). 9 | 10 | loop(MM) -> 11 | receive 12 | {chan, MM, Msg} -> 13 | chat_server ! {mm, MM, Msg}, 14 | loop(MM); 15 | {'EXIT', MM, _Why} -> 16 | chat_server ! {mm_closed, MM}; 17 | Other -> 18 | io:format("mod_chat_controller unexpected message =~p (MM=~p)~n", 19 | [Other, MM]), 20 | loop(MM) 21 | end. 22 | 23 | -------------------------------------------------------------------------------- /code/socket_dist/mod_echo.erl: -------------------------------------------------------------------------------- 1 | -module(mod_echo). 2 | 3 | -export([start/3]). 4 | 5 | start(MM, ArgC, ArgS) -> 6 | io:format("Echo starting arguments ArgC:~p ArgS:~p~n", 7 | [ArgC, ArgS]), 8 | loop(MM). 9 | 10 | loop(MM) -> 11 | receive 12 | {chan, MM, Any} -> 13 | MM ! {send, Any}, 14 | loop(MM); 15 | {chan_closed, MM} -> 16 | io:format("echo channel closed~n"), 17 | exit(normal); 18 | Any -> 19 | io:format("echo bad message:~p~n",[Any]), 20 | loop(MM) 21 | end. 22 | -------------------------------------------------------------------------------- /code/socket_dist/mod_math.erl: -------------------------------------------------------------------------------- 1 | -module(mod_math). 2 | -export([run/3]). 3 | 4 | run(MM, ArgC, ArgS) -> 5 | io:format("mod_math:run starting~n" 6 | "ArgC = ~p ArgS=~p~n",[ArgC, ArgS]), 7 | loop(MM). 8 | 9 | loop(MM) -> 10 | receive 11 | {chan, MM, {factorial, N}} -> 12 | MM ! {send, fac(N)}, 13 | loop(MM); 14 | {chan, MM, {fibonacci, N}} -> 15 | MM ! {send, fib(N)}, 16 | loop(MM); 17 | {chan_closed, MM} -> 18 | io:format("mod_math stopping~n"), 19 | exit(normal) 20 | end. 21 | 22 | fac(0) -> 1; 23 | fac(N) -> N*fac(N-1). 24 | 25 | fib(1) -> 1; 26 | fib(2) -> 1; 27 | fib(N) -> fib(N-1) + fib(N-2). 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /code/socket_dist/mod_name_server.erl: -------------------------------------------------------------------------------- 1 | -module(mod_name_server). 2 | -export([start_me_up/3]). 3 | 4 | start_me_up(MM, _ArgsC, _ArgS) -> 5 | loop(MM). 6 | 7 | loop(MM) -> 8 | receive 9 | {chan, MM, {store, K, V}} -> 10 | kvs:store(K, V), 11 | loop(MM); 12 | {chan, MM, {lookup, K}} -> 13 | MM ! {send, kvs:lookup(K)}, 14 | loop(MM); 15 | {chan_closed, MM} -> 16 | true 17 | end. 18 | -------------------------------------------------------------------------------- /code/socket_dist/mod_srpc.erl: -------------------------------------------------------------------------------- 1 | -module(mod_srpc). 2 | -export([start/3, rpc/4]). 3 | -import(lists, [member/2]). 4 | -import(lib_chan_mm, [send/2, close/1]). 5 | 6 | start(MM, _Client, Allow) -> 7 | server_loop(MM, Allow). 8 | 9 | server_loop(MM, Allow) -> 10 | receive 11 | {chan, MM, {rpc, Mod, Func, Args}} -> 12 | case member(Mod, Allow) of 13 | true -> 14 | case (catch apply(Mod, Func, Args)) of 15 | {'EXIT', Why} -> 16 | send(MM, {rpcExit, Why}), 17 | close(MM); 18 | Ret -> 19 | send(MM, {rpcOK, Ret}), 20 | server_loop(MM, Allow) 21 | end; 22 | false -> 23 | send(MM, {exit, {modNotAllowed, Mod}}), 24 | close(MM) 25 | end; 26 | {chan, MM, {call, Mod, Func, Args}} -> 27 | case member(Mod, Allow) of 28 | true -> 29 | apply(Mod, Func, Args), 30 | server_loop(MM, Allow); 31 | false -> 32 | send(MM, {exit, badMod}), 33 | close(MM) 34 | end; 35 | {chan_closed, MM} -> 36 | %% io:format("server closed connection~n"), 37 | true; 38 | Other -> 39 | io:format("unexpected message:~p~n",[Other]), 40 | server_loop(MM, Allow) 41 | end. 42 | 43 | rpc(MM, M, F, A) -> 44 | send(MM, {rpc, M, F, A}), 45 | receive 46 | {chan, MM, {rpcOK, R}} -> 47 | R; 48 | {chan, MM, {rpcExit, Why}} -> 49 | exit(Why); 50 | {chan, MM, {exit, Why}} -> 51 | exit(Why); 52 | Other -> 53 | io:format("Glurkkkk~p~n",[Other]) 54 | end. 55 | -------------------------------------------------------------------------------- /code/socket_dist/readme: -------------------------------------------------------------------------------- 1 | Files names 2 | 3 | test1 -- test function 4 | any_apply -- apply any function 5 | 6 | mod_* Plugings for lib_auth_spawn 7 | 8 | lib_* Auth spawn and related routines 9 | 10 | lib_auth_spawn.erl 11 | lib_auth.erl 12 | lib_auth_test.erl 13 | lib_tcp_cs.erl 14 | lib_tcp_mm.erl 15 | 16 | irc_client.erl -- client controller 17 | irc_client_widget.erl -- client widget 18 | irc_group_controller.erl -- group controller 19 | irc_server.erl -- server 20 | mod_irc_controller.erl -- plugin for auth 21 | 22 | start_server() ${HOME}/.erlang_config/auth_spawn.conf) 23 | start_server(Conf) 24 | 25 | {port, 1234}. 26 | {service, srpc, password, "secret", mfa, mod_srpc, start, [test1]}. 27 | {service, root, password, "verySecret", mfa, mod_srpc, start, [any_apply]}. 28 | {service, test1, password, "test1", mfa, mod_test1, start, {hi, joe}}. 29 | {service, echo, password, "echo1", mfa, mod_echo, start, {server,args}}. 30 | {service, fac, password, "facSecret", mfa, mod_fac, run, []}. 31 | 32 | Client code: 33 | 34 | connect([{Host,Port,Service,Pwd}]) -> {ok, Pid} | error 35 | connect(Conf) 36 | connect() 37 | 38 | rpc(Pid, Mod, Func, Args) 39 | cast(Pid, Mod, Func, Args) 40 | stop(Pid) 41 | 42 | -------------------------------------------------------------------------------- /code/socket_dist/test1.erl: -------------------------------------------------------------------------------- 1 | -module(test1). 2 | 3 | -export([fac/1]). 4 | 5 | fac(0) -> 1; 6 | fac(N) -> N*fac(N-1). 7 | -------------------------------------------------------------------------------- /code/socket_dist/test_name_server.erl: -------------------------------------------------------------------------------- 1 | -module(test_name_server). 2 | 3 | -compile(export_all). 4 | 5 | %% tests the examples in the book 6 | %% ** remember the code path must be able to find this directory 7 | %% and the code directory (above this) 8 | 9 | test(1) -> 10 | kvs:start(), 11 | lib_chan:start_server(); 12 | 13 | test(2) -> 14 | {ok, P} = lib_chan:connect("localhost", 1234, nameServer, "ABXy45", nil), 15 | io:format("ok P=~p~n",[P]), 16 | lib_chan_mm:trace_with_tag(P, junk), 17 | lib_chan:cast(P, {store, "joe", "writing a book"}), 18 | V1 = lib_chan:rpc(P, {lookup, "joe"}), 19 | V2 = lib_chan:rpc(P, {lookup, fred}), 20 | {V1, V2}. 21 | 22 | -------------------------------------------------------------------------------- /code/socket_examples.erl: -------------------------------------------------------------------------------- 1 | -module(socket_examples). 2 | -compile(export_all). 3 | -import(lists, [reverse/1]). 4 | 5 | 6 | nano_get_url() -> 7 | nano_get_url("www.google.com"). 8 | 9 | nano_get_url(Host) -> 10 | {ok,Socket} = gen_tcp:connect(Host,80,[binary, {packet, 0}]), %% (1) 11 | ok = gen_tcp:send(Socket, "GET / HTTP/1.0\r\n\r\n"), %% (2) 12 | receive_data(Socket, []). 13 | 14 | receive_data(Socket, SoFar) -> 15 | receive 16 | {tcp,Socket,Bin} -> %% (3) 17 | receive_data(Socket, [Bin|SoFar]); 18 | {tcp_closed,Socket} -> %% (4) 19 | list_to_binary(reverse(SoFar)) %% (5) 20 | end. 21 | 22 | 23 | 24 | nano_client_eval(Str) -> 25 | {ok, Socket} = 26 | gen_tcp:connect("localhost", 2345, 27 | [binary, {packet, 4}]), 28 | ok = gen_tcp:send(Socket, term_to_binary(Str)), 29 | receive 30 | {tcp,Socket,Bin} -> 31 | io:format("Client received binary = ~p~n",[Bin]), 32 | Val = binary_to_term(Bin), 33 | io:format("Client result = ~p~n",[Val]), 34 | gen_tcp:close(Socket) 35 | end. 36 | 37 | 38 | 39 | start_nano_server() -> 40 | {ok, Listen} = gen_tcp:listen(2345, [binary, {packet, 4}, %% (6) 41 | {reuseaddr, true}, 42 | {active, true}]), 43 | {ok, Socket} = gen_tcp:accept(Listen), %% (7) 44 | gen_tcp:close(Listen), %% (8) 45 | loop(Socket). 46 | 47 | loop(Socket) -> 48 | receive 49 | {tcp, Socket, Bin} -> 50 | io:format("Server received binary = ~p~n",[Bin]), 51 | Str = binary_to_term(Bin), %% (9) 52 | io:format("Server (unpacked) ~p~n",[Str]), 53 | Reply = lib_misc:string2value(Str), %% (10) 54 | io:format("Server replying = ~p~n",[Reply]), 55 | gen_tcp:send(Socket, term_to_binary(Reply)), %% (11) 56 | loop(Socket); 57 | {tcp_closed, Socket} -> 58 | io:format("Server socket closed~n") 59 | end. 60 | 61 | 62 | 63 | error_test() -> 64 | spawn(fun() -> error_test_server() end), 65 | lib_misc:sleep(2000), 66 | {ok,Socket} = gen_tcp:connect("localhost",4321,[binary, {packet, 2}]), 67 | io:format("connected to:~p~n",[Socket]), 68 | gen_tcp:send(Socket, <<"123">>), 69 | receive 70 | Any -> 71 | io:format("Any=~p~n",[Any]) 72 | end. 73 | 74 | error_test_server() -> 75 | {ok, Listen} = gen_tcp:listen(4321, [binary,{packet,2}]), 76 | {ok, Socket} = gen_tcp:accept(Listen), 77 | error_test_server_loop(Socket). 78 | 79 | error_test_server_loop(Socket) -> 80 | receive 81 | {tcp, Socket, Data} -> 82 | io:format("received:~p~n",[Data]), 83 | atom_to_list(Data), 84 | error_test_server_loop(Socket) 85 | end. 86 | 87 | -------------------------------------------------------------------------------- /code/status.erl: -------------------------------------------------------------------------------- 1 | -module(status). 2 | 3 | -compile(export_all). 4 | -import(lists, [filter/2, map/2, member/2]). 5 | 6 | start() -> 7 | not_compiled(). 8 | 9 | not_compiled() -> 10 | E = lib_files_find:files(".", "*.erl",false), 11 | E1 = map(fun(I) -> filename:rootname(I) end, E), 12 | B = lib_files_find:files(".", "*.beam",false), 13 | B1 = map(fun(I) -> filename:rootname(I) end, B), 14 | filter(fun(I) -> not member(I, B1) end, E1). 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /code/stdmacros.hrl: -------------------------------------------------------------------------------- 1 | 2 | -define(IN(X,Min,Max), X >= Min, X =< Max). 3 | -define(LOWER(X), when X >= $a, X =< $z). 4 | -define(DIGIT(X), X >= $0, X =< $9). 5 | -------------------------------------------------------------------------------- /code/stimer.erl: -------------------------------------------------------------------------------- 1 | -module(stimer). 2 | -export([start/2, cancel/1]). 3 | 4 | start(Time, Fun) -> spawn(fun() -> timer(Time, Fun) end). 5 | 6 | cancel(Pid) -> Pid ! cancel. 7 | 8 | timer(Time, Fun) -> 9 | receive 10 | cancel -> 11 | void 12 | after Time -> 13 | Fun() 14 | end. 15 | -------------------------------------------------------------------------------- /code/terms1.dat: -------------------------------------------------------------------------------- 1 | {apples, 200}. 2 | {bananas, 23}. 3 | {grapes, 45678}. 4 | {peanuts, 6789}. 5 | {sausages, 234}. 6 | -------------------------------------------------------------------------------- /code/terms1.dat.tmp: -------------------------------------------------------------------------------- 1 | {apples,200}. 2 | {bananas,18}. 3 | {grapes,45678}. 4 | {peanuts,6789}. 5 | {sausages,234}. 6 | -------------------------------------------------------------------------------- /code/test1.erl: -------------------------------------------------------------------------------- 1 | -module(test1). 2 | -export([foo/1]). 3 | 4 | zip(X) -> 5 | % this is zip 6 | Y = "abc", 7 | case X of 8 | 1 -> 9 | 2 10 | end. 11 | 12 | 13 | foo(X) -> 14 | 15 | bar(X). 16 | 17 | 18 | 19 | bar(X) -> 20 | 42. 21 | 22 | -------------------------------------------------------------------------------- /code/test_mapreduce.erl: -------------------------------------------------------------------------------- 1 | -module(test_mapreduce). 2 | -compile(export_all). 3 | -import(lists, [reverse/1, sort/1]). 4 | 5 | test() -> 6 | wc_dir("."). 7 | 8 | wc_dir(Dir) -> 9 | F1 = fun generate_words/2, 10 | F2 = fun count_words/3, 11 | Files = lib_find:files(Dir, "*.erl", false), 12 | L1 = phofs:mapreduce(F1, F2, [], Files), 13 | reverse(sort(L1)). 14 | 15 | generate_words(Pid, File) -> 16 | F = fun(Word) -> Pid ! {Word, 1} end, 17 | lib_misc:foreachWordInFile(File, F). 18 | 19 | count_words(Key, Vals, A) -> 20 | [{length(Vals), Key}|A]. 21 | -------------------------------------------------------------------------------- /code/test_mnesia.erl: -------------------------------------------------------------------------------- 1 | -module(test_mnesia). 2 | -import(lists, [foreach/2]). 3 | -compile(export_all). 4 | 5 | %% IMPORTANT: The next line must be included 6 | %% if we want to call qlc:q(...) 7 | 8 | -include_lib("stdlib/include/qlc.hrl"). 9 | 10 | 11 | -record(shop, {item, quantity, cost}). 12 | -record(cost, {name, price}). 13 | 14 | 15 | -record(design, {id, plan}). 16 | 17 | 18 | 19 | do_this_once() -> 20 | mnesia:create_schema([node()]), 21 | mnesia:start(), 22 | mnesia:create_table(shop, [{attributes, record_info(fields, shop)}]), 23 | mnesia:create_table(cost, [{attributes, record_info(fields, cost)}]), 24 | mnesia:create_table(design, [{attributes, record_info(fields, design)}]), 25 | mnesia:stop(). 26 | 27 | 28 | start() -> 29 | mnesia:start(), 30 | mnesia:wait_for_tables([shop,cost,design], 20000). 31 | 32 | 33 | 34 | %% SQL equivalent 35 | %% SELECT * FROM shop; 36 | 37 | demo(select_shop) -> 38 | do(qlc:q([X || X <- mnesia:table(shop)])); 39 | 40 | 41 | 42 | 43 | %% SQL equivalent 44 | %% SELECT item, quantity FROM shop; 45 | 46 | demo(select_some) -> 47 | do(qlc:q([{X#shop.item, X#shop.quantity} || X <- mnesia:table(shop)])); 48 | 49 | 50 | 51 | 52 | %% SQL equivalent 53 | %% SELECT shop.item FROM shop 54 | %% WHERE shop.quantity < 250; 55 | 56 | demo(reorder) -> 57 | do(qlc:q([X#shop.item || X <- mnesia:table(shop), 58 | X#shop.quantity < 250 59 | ])); 60 | 61 | 62 | %% SQL equivalent 63 | %% SELECT shop.item 64 | %% FROM shop, cost 65 | %% WHERE shop.item = cost.name 66 | %% AND cost.price < 2 67 | %% AND shop.quantity < 250 68 | 69 | demo(join) -> 70 | do(qlc:q([X#shop.item || X <- mnesia:table(shop), 71 | X#shop.quantity < 250, 72 | Y <- mnesia:table(cost), 73 | X#shop.item =:= Y#cost.name, 74 | Y#cost.price < 2 75 | ])). 76 | 77 | 78 | 79 | do(Q) -> 80 | F = fun() -> qlc:e(Q) end, 81 | {atomic, Val} = mnesia:transaction(F), 82 | Val. 83 | 84 | 85 | 86 | 87 | example_tables() -> 88 | [%% The shop table 89 | {shop, apple, 20, 2.3}, 90 | {shop, orange, 100, 3.8}, 91 | {shop, pear, 200, 3.6}, 92 | {shop, banana, 420, 4.5}, 93 | {shop, potato, 2456, 1.2}, 94 | %% The cost table 95 | {cost, apple, 1.5}, 96 | {cost, orange, 2.4}, 97 | {cost, pear, 2.2}, 98 | {cost, banana, 1.5}, 99 | {cost, potato, 0.6} 100 | ]. 101 | 102 | 103 | 104 | add_shop_item(Name, Quantity, Cost) -> 105 | Row = #shop{item=Name, quantity=Quantity, cost=Cost}, 106 | F = fun() -> 107 | mnesia:write(Row) 108 | end, 109 | mnesia:transaction(F). 110 | 111 | 112 | 113 | remove_shop_item(Item) -> 114 | Oid = {shop, Item}, 115 | F = fun() -> 116 | mnesia:delete(Oid) 117 | end, 118 | mnesia:transaction(F). 119 | 120 | 121 | 122 | 123 | farmer(Nwant) -> 124 | %% Nwant = Number of oranges the farmer wants to buy 125 | F = fun() -> 126 | %% find the number of apples 127 | [Apple] = mnesia:read({shop,apple}), 128 | Napples = Apple#shop.quantity, 129 | Apple1 = Apple#shop{quantity = Napples + 2*Nwant}, 130 | %% update the database 131 | mnesia:write(Apple1), 132 | %% find the number of oranges 133 | [Orange] = mnesia:read({shop,orange}), 134 | NOranges = Orange#shop.quantity, 135 | if 136 | NOranges >= Nwant -> 137 | N1 = NOranges - Nwant, 138 | Orange1 = Orange#shop{quantity=N1}, 139 | %% update the database 140 | mnesia:write(Orange1); 141 | true -> 142 | %% Oops -- not enough oranges 143 | mnesia:abort(oranges) 144 | end 145 | end, 146 | mnesia:transaction(F). 147 | 148 | 149 | 150 | 151 | reset_tables() -> 152 | mnesia:clear_table(shop), 153 | mnesia:clear_table(cost), 154 | F = fun() -> 155 | foreach(fun mnesia:write/1, example_tables()) 156 | end, 157 | mnesia:transaction(F). 158 | 159 | 160 | 161 | 162 | 163 | add_plans() -> 164 | D1 = #design{id = {joe,1}, 165 | plan = {circle,10}}, 166 | D2 = #design{id = fred, 167 | plan = {rectangle,10,5}}, 168 | D3 = #design{id = {jane,{house,23}}, 169 | plan = {house, 170 | [{floor,1, 171 | [{doors,3}, 172 | {windows,12}, 173 | {rooms,5}]}, 174 | {floor,2, 175 | [{doors,2}, 176 | {rooms,4}, 177 | {windows,15}]}]}}, 178 | F = fun() -> 179 | mnesia:write(D1), 180 | mnesia:write(D2), 181 | mnesia:write(D3) 182 | end, 183 | mnesia:transaction(F). 184 | 185 | 186 | 187 | get_plan(PlanId) -> 188 | F = fun() -> mnesia:read({design, PlanId}) end, 189 | mnesia:transaction(F). 190 | 191 | 192 | -------------------------------------------------------------------------------- /code/tracer_test.erl: -------------------------------------------------------------------------------- 1 | -module(tracer_test). 2 | 3 | -include_lib("stdlib/include/ms_transform.hrl"). 4 | -compile(export_all). 5 | 6 | %% www.otp.org/doc/man/ms_transform.html 7 | 8 | 9 | test1() -> 10 | dbg:tracer(), 11 | dbg:tpl(tracer_test,fib,'_', 12 | dbg:fun2ms(fun(_) -> return_trace() end)), 13 | dbg:p(all,[c]), 14 | tracer_test:fib(4). 15 | 16 | 17 | 18 | test2() -> 19 | trace_module(tracer_test, fun() -> fib(4) end). 20 | 21 | fib(0) -> 1; 22 | fib(1) -> 1; 23 | fib(N) -> fib(N-1) + fib(N-2). 24 | 25 | 26 | 27 | trace_module(Mod, StartFun) -> 28 | %% We'll spawn a process to do the tracing 29 | spawn(fun() -> trace_module1(Mod, StartFun) end). 30 | 31 | trace_module1(Mod, StartFun) -> 32 | %% The next line says: trace all function calls and return 33 | %% values in Mod 34 | erlang:trace_pattern({Mod, '_','_'}, 35 | [{'_',[],[{return_trace}]}], 36 | [local]), 37 | %% spawn a function to do the tracing 38 | S = self(), 39 | Pid = spawn(fun() -> do_trace(S, StartFun) end), 40 | %% setup the trace. Tell the system to start tracing 41 | %% the process Pid 42 | erlang:trace(Pid, true, [call,procs]), 43 | %% Now tell Pid to start 44 | Pid ! {self(), start}, 45 | trace_loop(). 46 | 47 | %% do_trace evaluates StartFun() 48 | %% when it is told to do so by Parent 49 | do_trace(Parent, StartFun) -> 50 | receive 51 | {Parent, start} -> 52 | StartFun() 53 | end. 54 | 55 | %% trace_loop displays the function call and return values 56 | trace_loop() -> 57 | receive 58 | {trace,_,call, X} -> 59 | io:format("Call: ~p~n",[X]), 60 | trace_loop(); 61 | {trace,_,return_from, Call, Ret} -> 62 | io:format("Return From: ~p => ~p~n",[Call, Ret]), 63 | trace_loop(); 64 | Other -> 65 | %% we get some other message - print them 66 | io:format("Other = ~p~n",[Other]), 67 | trace_loop() 68 | end. 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /code/try_test.erl: -------------------------------------------------------------------------------- 1 | -module(try_test). 2 | -compile(export_all). 3 | 4 | 5 | generate_exception(1) -> a; 6 | generate_exception(2) -> throw(a); 7 | generate_exception(3) -> exit(a); 8 | generate_exception(4) -> {'EXIT', a}; 9 | generate_exception(5) -> erlang:error(a). 10 | 11 | 12 | 13 | demo1() -> 14 | [catcher(I) || I <- [1,2,3,4,5]]. 15 | 16 | catcher(N) -> 17 | try generate_exception(N) of 18 | Val -> {N, normal, Val} 19 | catch 20 | throw:X -> {N, caught, thrown, X}; 21 | exit:X -> {N, caught, exited, X}; 22 | error:X -> {N, caught, error, X} 23 | end. 24 | 25 | 26 | 27 | demo2() -> 28 | [{I, (catch generate_exception(I))} || I <- [1,2,3,4,5]]. 29 | 30 | 31 | 32 | demo3() -> 33 | try generate_exception(5) 34 | catch 35 | error:X -> 36 | {X, erlang:get_stacktrace()} 37 | end. 38 | 39 | 40 | 41 | lookup(N) -> 42 | case(N) of 43 | 1 -> {'EXIT', a}; 44 | 2 -> exit(a) 45 | end. 46 | 47 | -------------------------------------------------------------------------------- /code/udp_test.erl: -------------------------------------------------------------------------------- 1 | -module(udp_test). 2 | -export([start_server/0, client/1]). 3 | 4 | start_server() -> 5 | spawn(fun() -> server(4000) end). 6 | 7 | %% The server 8 | server(Port) -> 9 | {ok, Socket} = gen_udp:open(Port, [binary]), 10 | io:format("server opened socket:~p~n",[Socket]), 11 | loop(Socket). 12 | 13 | loop(Socket) -> 14 | receive 15 | {udp, Socket, Host, Port, Bin} = Msg -> 16 | io:format("server received:~p~n",[Msg]), 17 | N = binary_to_term(Bin), 18 | Fac = fac(N), 19 | gen_udp:send(Socket, Host, Port, term_to_binary(Fac)), 20 | loop(Socket) 21 | end. 22 | 23 | fac(0) -> 1; 24 | fac(N) -> N * fac(N-1). 25 | 26 | %% The client 27 | 28 | client(N) -> 29 | {ok, Socket} = gen_udp:open(0, [binary]), 30 | io:format("client opened socket=~p~n",[Socket]), 31 | ok = gen_udp:send(Socket, "localhost", 4000, 32 | term_to_binary(N)), 33 | Value = receive 34 | {udp, Socket, _, _, Bin} = Msg -> 35 | io:format("client received:~p~n",[Msg]), 36 | binary_to_term(Bin) 37 | after 2000 -> 38 | 0 39 | end, 40 | gen_udp:close(Socket), 41 | Value. 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /code/upcase.erl: -------------------------------------------------------------------------------- 1 | -module(upcase). 2 | -export([start/0]). 3 | 4 | start() -> 5 | case io:get_chars('', 8192) of 6 | eof -> 7 | init:stop(); 8 | Str -> 9 | Str1 = up_case(Str), 10 | io:put_chars(Str1), 11 | start() 12 | end. 13 | 14 | up_case([H|T]) when $a =< H, H =< $z -> 15 | [H-$a+$A|up_case(T)]; 16 | up_case([H|T]) -> 17 | [H|up_case(T)]; 18 | up_case([]) -> 19 | []. 20 | -------------------------------------------------------------------------------- /code/update_binary_file.erl: -------------------------------------------------------------------------------- 1 | -module(update_binary_file). 2 | -export([update/3]). 3 | 4 | update(File, Key, Delta) -> 5 | {ok, Bin} = file:read_file(File), 6 | Terms = binary_to_term(Bin), 7 | Terms1 = do_update(Key, Delta, Terms), 8 | file:write_file(File, [term_to_binary(Terms1)]). 9 | 10 | do_update(Key, Delta, [{Key,Val}|T]) -> 11 | [{Key,Val+Delta}|T]; 12 | do_update(Key, Delta, [H|T]) -> 13 | [H|do_update(Key, Delta, T)]; 14 | do_update(Key, Delta, []) -> 15 | [{Key, Delta}]. 16 | -------------------------------------------------------------------------------- /code/update_file.erl: -------------------------------------------------------------------------------- 1 | -module(update_file). 2 | -export([update/3]). 3 | -import(lib_misc, [unconsult/2]). 4 | 5 | update(File, Key, Delta) -> 6 | {ok, Terms} = file:consult(File), 7 | Terms1 = do_update(Key, Delta, Terms), 8 | unconsult(File ++ ".tmp", Terms1). %%