-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathname_generator.erl
More file actions
173 lines (148 loc) · 6.13 KB
/
name_generator.erl
File metadata and controls
173 lines (148 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
%%-----------------------------------------------------------------
%% @doc
%% Erlang implementation of the name-generator shell script.
%% It respects the following environment variables:
%% SEPARATOR – string placed between adjective and noun (default "-")
%% counto – number of names to emit (default: terminal height)
%% NOUN_FOLDER – folder containing noun files (default: $PWD/nouns)
%% ADJ_FOLDER – folder containing adjective files (default: $PWD/adjectives)
%% NOUN_FILE – specific noun file to use (default: random file from NOUN_FOLDER)
%% ADJ_FILE – specific adjective file to use (default: random file from ADJ_FOLDER)
%% DEBUG – if set to "true", prints debugging information.
%%-----------------------------------------------------------------
-module(name_generator).
-export([main/0, name_generator/0]).
-include_lib("kernel/include/file.hrl").
%%===================================================================
%% Entry point
%%===================================================================
main() ->
%% Seed the random generator
_ = rand:seed(exsplus, now_timestamp()),
Separator = get_env("SEPARATOR", "-"),
CountO = get_counto(),
NounFolder = get_env_path("NOUN_FOLDER", default_noun_folder()),
AdjFolder = get_env_path("ADJ_FOLDER", default_adj_folder()),
NounFile = get_env_path("NOUN_FILE", pick_random_file(NounFolder)),
AdjFile = get_env_path("ADJ_FILE", pick_random_file(AdjFolder)),
Debug = get_env("DEBUG", "false") =:= "true",
NounLines = read_nonempty_lines(NounFile),
AdjLines = read_nonempty_lines(AdjFile),
generate_names(CountO, Separator, NounLines, AdjLines, Debug,
NounFile, AdjFile, NounFolder, AdjFolder).
%% Provide a function named after the module for compatibility with
%% `erl -noshell -s name_generator name_generator` style invocations.
name_generator() ->
main().
%%===================================================================
%% Helpers
%%===================================================================
%% Get an environment variable or return Default.
get_env(Var, Default) ->
case os:getenv(Var) of
false -> Default;
Value -> Value
end.
%% Get an environment variable interpreted as a path, or return DefaultPath.
get_env_path(Var, DefaultPath) ->
case os:getenv(Var) of
false -> DefaultPath;
"" -> DefaultPath;
Value -> filename:absname(Value)
end.
%% Resolve the current working directory.
default_cwd() ->
{ok, Cwd} = file:get_cwd(),
Cwd.
default_noun_folder() ->
filename:join(default_cwd(), "nouns").
default_adj_folder() ->
filename:join(default_cwd(), "adjectives").
%% Determine how many names to emit.
get_counto() ->
case os:getenv("counto") of
false -> terminal_lines();
"" -> terminal_lines();
Str ->
case string:to_integer(string:trim(Str)) of
{error, _} -> terminal_lines();
{Int, _} -> Int
end
end.
%% Try to obtain terminal height via `tput lines`; fallback to 24.
terminal_lines() ->
case os:cmd("tput lines") of
"" -> 24;
Out ->
case string:to_integer(string:trim(Out)) of
{error, _} -> 24;
{Int, _} -> Int
end
end.
%% Pick a random regular file from a folder.
pick_random_file(Folder) ->
case list_regular_files(Folder) of
[] -> erlang:error({no_regular_files, Folder});
Files -> lists:nth(rand_index(length(Files)), Files)
end.
list_regular_files(Folder) ->
case file:list_dir(Folder) of
{ok, Entries} ->
[ filename:join(Folder, E)
|| E <- Entries,
filelib:is_regular(filename:join(Folder, E)) ];
{error, _} -> []
end.
rand_index(N) when N > 0 ->
rand:uniform(N).
%% Read a file and return a list of non‑empty trimmed lines.
read_nonempty_lines(FilePath) ->
case file:read_file(FilePath) of
{ok, Bin} ->
Content = unicode:characters_to_list(Bin),
Lines = string:split(Content, "\n", all),
[ string:trim(L) || L <- Lines, string:trim(L) =/= "" ];
{error, Reason} ->
erlang:error({cannot_read_file, FilePath, Reason})
end.
%% Generate and print the required number of names.
generate_names(0, _Sep, _NounLines, _AdjLines, _Debug,
_NounFile, _AdjFile, _NounFolder, _AdjFolder) ->
ok;
generate_names(Count, Sep, NounLines, AdjLines, Debug,
NounFile, AdjFile, NounFolder, AdjFolder) ->
NounRaw = random_line(NounLines),
AdjRaw = random_line(AdjLines),
Noun = string:to_lower(NounRaw),
Adj = AdjRaw,
maybe_debug(Debug, Adj, Noun, AdjFile, AdjFolder,
NounFile, NounFolder, Count, Count),
io:format("~s~s~s~n", [Adj, Sep, Noun]),
generate_names(Count - 1, Sep, NounLines, AdjLines, Debug,
NounFile, AdjFile, NounFolder, AdjFolder).
random_line(Lines) ->
Index = rand_index(length(Lines)),
lists:nth(Index, Lines).
maybe_debug(true, Adj, Noun, AdjFile, AdjFolder,
NounFile, NounFolder, CountZero, CountO) ->
io:format("DEBUG:~n", []),
io:format(" adjective : ~s~n", [Adj]),
io:format(" noun : ~s~n", [Noun]),
io:format(" ADJ_FILE : ~s~n", [AdjFile]),
io:format(" ADJ_FOLDER: ~s~n", [AdjFolder]),
io:format(" NOUN_FILE : ~s~n", [NounFile]),
io:format(" NOUN_FOLDER: ~s~n", [NounFolder]),
io:format(" ~p > ~p~n", [CountZero, CountO]);
maybe_debug(false, _Adj, _Noun, _AdjFile, _AdjFolder,
_NounFile, _NounFolder, _CountZero, _CountO) ->
ok.
%% Helper to get a timestamp suitable for rand:seed/2.
%% Uses erlang:system_time/1 to avoid the deprecated erlang:now/0.
now_timestamp() ->
%% Get current time in microseconds.
Microseconds = erlang:system_time(microsecond),
%% Split into mega‑seconds, seconds, and remaining microseconds.
Mega = Microseconds div 1000000 div 1000000,
Sec = (Microseconds div 1000000) rem 1000000,
Micro = Microseconds rem 1000000,
{Mega, Sec, Micro}.