1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(win32reg). 21 22-export([open/1, close/1, 23 current_key/1, change_key/2, change_key_create/2, 24 sub_keys/1, delete_key/1, 25 value/2, values/1, set_value/3, delete_value/2, 26 expand/1, 27 format_error/1]). 28 29-export_type([reg_handle/0]). 30 31%% Key handles (always open). 32-define(hkey_classes_root, 16#80000000). 33-define(hkey_current_user, 16#80000001). 34-define(hkey_local_machine, 16#80000002). 35-define(hkey_users, 16#80000003). 36-define(hkey_performance_data, 16#80000004). 37-define(hkey_current_config, 16#80000005). 38-define(hkey_dyn_data, 16#80000006). 39 40%% Driver commands. 41-define(cmd_get_current, 0). 42-define(cmd_open_key, 1). 43-define(cmd_create_key, 2). 44-define(cmd_get_all_subkeys, 3). 45-define(cmd_get_value, 4). 46-define(cmd_get_all_values, 5). 47-define(cmd_set_value, 6). 48-define(cmd_delete_key, 7). 49-define(cmd_delete_value, 8). 50 51%% Data types. 52-define(reg_sc, 1). 53-define(reg_expand_sc, 2). 54-define(reg_binary, 3). 55-define(reg_dword, 4). 56 57%% Basic types internal to this file. 58-opaque reg_handle() :: {'win32reg',port()}. 59-type name() :: string() | 'default'. 60-type value() :: string() | integer() | binary(). 61 62%%% Exported functions. 63 64-spec open(OpenModeList) -> ReturnValue when 65 OpenModeList :: [OpenMode], 66 OpenMode :: 'read' | 'write', 67 ReturnValue :: {'ok', RegHandle} | {'error', ErrorId :: 'enotsup'}, 68 RegHandle :: reg_handle(). 69 70open(Modes) -> 71 case os:type() of 72 {win32, _} -> 73 case open_mode(Modes, []) of 74 {error, Reason} -> 75 {error, Reason}; 76 ModeStr -> 77 P = open_port({spawn, "registry__drv__ " ++ ModeStr}, []), 78 {ok, {win32reg, P}} 79 end; 80 _ -> 81 {error, enotsup} 82 end. 83 84-spec close(RegHandle) -> 'ok' when 85 RegHandle :: reg_handle(). 86 87close({win32reg, Reg}) when is_port(Reg) -> 88 unlink(Reg), 89 exit(Reg, die), 90 ok. 91 92-spec current_key(RegHandle) -> ReturnValue when 93 RegHandle :: reg_handle(), 94 ReturnValue :: {'ok', string()}. 95 96current_key({win32reg, Reg}) when is_port(Reg) -> 97 Cmd = [?cmd_get_current], 98 Reg ! {self(), {command, Cmd}}, 99 {state, Hkey, Name} = get_result(Reg), 100 Root = hkey_to_string(Hkey), 101 {ok, case Name of 102 [] -> Root; 103 _ -> Root ++ [$\\|Name] 104 end}. 105 106-spec change_key(RegHandle, Key) -> ReturnValue when 107 RegHandle :: reg_handle(), 108 Key :: string(), 109 ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. 110 111change_key({win32reg, Reg}, Key) when is_port(Reg) -> 112 change_key(Reg, ?cmd_open_key, Key). 113 114-spec change_key_create(RegHandle, Key) -> ReturnValue when 115 RegHandle :: reg_handle(), 116 Key :: string(), 117 ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. 118 119change_key_create({win32reg, Reg}, Key) when is_port(Reg) -> 120 change_key(Reg, ?cmd_create_key, Key). 121 122change_key(Reg, Cmd, Key) -> 123 case parse_key(Key, Reg) of 124 {ok, Hkey, Path} -> 125 Reg ! {self(), {command, [Cmd, i32(Hkey), Path, 0]}}, 126 get_result(Reg); 127 {error, Reason} -> 128 {error, Reason} 129 end. 130 131-spec sub_keys(RegHandle) -> ReturnValue when 132 RegHandle :: reg_handle(), 133 ReturnValue :: {'ok', [SubKey]} | {'error', ErrorId :: atom()}, 134 SubKey :: string(). 135 136sub_keys({win32reg, Reg}) when is_port(Reg) -> 137 Cmd = [?cmd_get_all_subkeys], 138 Reg ! {self(), {command, Cmd}}, 139 collect_keys(Reg, []). 140 141-spec delete_key(RegHandle) -> ReturnValue when 142 RegHandle :: reg_handle(), 143 ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. 144 145delete_key({win32reg, Reg}) when is_port(Reg) -> 146 Cmd = [?cmd_delete_key], 147 Reg ! {self(), {command, Cmd}}, 148 get_result(Reg). 149 150-spec set_value(RegHandle, Name, Value) -> ReturnValue when 151 RegHandle :: reg_handle(), 152 Name :: name(), 153 Value :: value(), 154 ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. 155 156set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> 157 Name = 158 case Name0 of 159 default -> []; 160 _ -> Name0 161 end, 162 {Type, V} = term_to_value(Value), 163 Cmd = [?cmd_set_value, Type, Name, 0, V], 164 Reg ! {self(), {command, Cmd}}, 165 get_result(Reg). 166 167-spec value(RegHandle, Name) -> ReturnValue when 168 RegHandle :: reg_handle(), 169 Name :: name(), 170 ReturnValue :: {'ok', Value :: value()} | {'error', ErrorId :: atom()}. 171 172value({win32reg, Reg}, Name) when is_port(Reg) -> 173 Cmd = [?cmd_get_value, Name, 0], 174 Reg ! {self(), {command, Cmd}}, 175 case get_result(Reg) of 176 {value, {Name, Value}} -> 177 {ok, Value}; 178 {error, Reason} -> 179 {error, Reason} 180 end. 181 182-spec values(RegHandle) -> ReturnValue when 183 RegHandle :: reg_handle(), 184 ReturnValue :: {'ok', [ValuePair]} | {'error', ErrorId :: atom()}, 185 ValuePair :: {Name :: name(), Value :: value()}. 186 187values({win32reg, Reg}) when is_port(Reg) -> 188 Cmd = [?cmd_get_all_values], 189 Reg ! {self(), {command, Cmd}}, 190 collect_values(Reg, []). 191 192-spec delete_value(RegHandle, Name) -> ReturnValue when 193 RegHandle :: reg_handle(), 194 Name :: name(), 195 ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. 196 197delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> 198 Name = 199 case Name0 of 200 default -> []; 201 _ -> Name0 202 end, 203 Cmd = [?cmd_delete_value, Name, 0], 204 Reg ! {self(), {command, Cmd}}, 205 get_result(Reg). 206 207-spec expand(String) -> ExpandedString when 208 String :: string(), 209 ExpandedString :: string(). 210 211expand(Value) -> 212 expand(Value, [], []). 213 214expand([$%, $%|Rest], [], Result) -> 215 expand(Rest, [], [$%|Result]); 216expand([$%, C|Rest], [], Result) -> 217 expand(Rest, [C], Result); 218expand([C|Rest], [], Result) -> 219 expand(Rest, [], [C|Result]); 220expand([$%|Rest], Env0, Result) -> 221 Env = lists:reverse(Env0), 222 expand(Rest, [], lists:reverse(os:getenv(Env, ""))++Result); 223expand([C|Rest], Env, Result) -> 224 expand(Rest, [C|Env], Result); 225expand([], [], Result) -> 226 lists:reverse(Result). 227 228-spec format_error(ErrorId) -> ErrorString when 229 ErrorId :: atom(), 230 ErrorString :: string(). 231 232format_error(ErrorId) -> 233 erl_posix_msg:message(ErrorId). 234 235%%% Implementation. 236 237-spec collect_values(port(), [{name(), value()}]) -> 238 {'ok', [{name(), value()}]} | {'error', ErrorId :: atom()}. 239 240collect_values(P, Result) -> 241 case get_result(P) of 242 ok -> 243 {ok, lists:reverse(Result)}; 244 {value, ValueData} -> 245 collect_values(P, [ValueData|Result]); 246 {error, Reason} -> 247 {error, Reason} 248 end. 249 250-spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', ErrorId :: atom()}. 251 252collect_keys(P, Result) -> 253 case get_result(P) of 254 ok -> 255 {ok, lists:reverse(Result)}; 256 {key, KeyData} -> 257 collect_keys(P, [KeyData|Result]); 258 {error, Reason} -> 259 {error, Reason} 260 end. 261 262get_result(P) -> 263 receive 264 {P, {data, Data}} -> 265 get_result1(Data) 266 end. 267 268get_result1([$e|Reason]) -> 269 {error, list_to_atom(Reason)}; 270get_result1([$o]) -> 271 ok; 272get_result1([$k|Name]) -> 273 {key, Name}; 274get_result1([$v|Rest0]) -> 275 {ok, Type, Rest1} = i32_on_head(Rest0), 276 {ok, Name0, Value} = get_cstring(Rest1), 277 Name = 278 case Name0 of 279 [] -> default; 280 _ -> Name0 281 end, 282 {value, {Name, encode_value(Type, Value)}}; 283get_result1([$s|Rest0]) -> 284 {ok, Hkey, Name} = i32_on_head(Rest0), 285 {state, Hkey, Name}. 286 287encode_value(?reg_sc, Value) -> 288 Value; 289encode_value(?reg_expand_sc, Value) -> 290 Value; 291encode_value(?reg_dword, Value) -> 292 i32(Value); 293encode_value(_, Value) -> 294 list_to_binary(Value). 295 296term_to_value(Int) when is_integer(Int) -> 297 {i32(?reg_dword), i32(Int)}; 298term_to_value(String) when is_list(String) -> 299 {i32(?reg_sc), [String, 0]}; 300term_to_value(Bin) when is_binary(Bin) -> 301 {i32(?reg_binary), Bin}; 302term_to_value(_) -> 303 exit(badarg). 304 305get_cstring(List) -> 306 get_cstring(List, []). 307 308get_cstring([0|Rest], Result) -> 309 {ok, lists:reverse(Result), Rest}; 310get_cstring([C|Rest], Result) -> 311 get_cstring(Rest, [C|Result]); 312get_cstring([], Result) -> 313 {ok, lists:reverse(Result), []}. 314 315i32(Int) when is_integer(Int) -> 316 [(Int bsr 24) band 255, 317 (Int bsr 16) band 255, 318 (Int bsr 8) band 255, 319 Int band 255]; 320i32([X1, X2, X3, X4]) -> 321 (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4. 322 323i32_on_head([X1, X2, X3, X4 | Rest]) -> 324 {ok, (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4, Rest}. 325 326parse_key([$\\|Rest], _) -> 327 parse_root(Rest, []); 328parse_key(Key, Reg) -> 329 parse_relative(Key, Reg). 330 331parse_relative(Path, Reg) -> 332 Cmd = [?cmd_get_current], 333 Reg ! {self(), {command, Cmd}}, 334 {state, RootHandle, Name} = get_result(Reg), 335 Original = split_key(Name), 336 Relative = lists:reverse(split_key(Path)), 337 case parse_relative1(Relative, Original) of 338 NewPath -> 339 {ok, RootHandle, NewPath} 340 %% XXX Error handling. 341 end. 342 343parse_relative1([".."|T1], [_|T2]) -> 344 parse_relative1(T1, T2); 345parse_relative1([Comp|Rest], Result) -> 346 parse_relative1(Rest, [Comp|Result]); 347parse_relative1([], Result) -> 348 reverse_and_join(Result, []). 349 350reverse_and_join([X|Rest], []) -> 351 reverse_and_join(Rest, [X]); 352reverse_and_join([X|Rest], Result) -> 353 reverse_and_join(Rest, [X, "\\" | Result]); 354reverse_and_join([], Result) -> 355 Result. 356 357split_key(Key) -> 358 split_key(Key, [], []). 359 360split_key([$\\|Rest], Current, Result) -> 361 split_key(Rest, [], [lists:reverse(Current)|Result]); 362split_key([C|Rest], Current, Result) -> 363 split_key(Rest, [C|Current], Result); 364split_key([], [], Result) -> 365 Result; 366split_key([], Current, Result) -> 367 [lists:reverse(Current)|Result]. 368 369parse_root([$\\|Rest], Result) -> 370 Root = 371 case lists:reverse(Result) of 372 [$h, $k, $e, $y, $_|Root0] -> 373 Root0; 374 Root0 -> 375 Root0 376 end, 377 case root_to_handle(list_to_atom(Root)) of 378 false -> 379 {error, enoent}; 380 Handle -> 381 {ok, Handle, Rest} 382 end; 383parse_root([C|Rest], Result) -> 384 parse_root(Rest, [C|Result]); 385parse_root([], Result) -> 386 parse_root([$\\], Result). 387 388root_to_handle(classes_root) -> ?hkey_classes_root; 389root_to_handle(hkcr) -> ?hkey_classes_root; 390root_to_handle(current_user) -> ?hkey_current_user; 391root_to_handle(hkcu) -> ?hkey_current_user; 392root_to_handle(local_machine) -> ?hkey_local_machine; 393root_to_handle(hklm) -> ?hkey_local_machine; 394root_to_handle(users) -> ?hkey_users; 395root_to_handle(hku) -> ?hkey_users; 396root_to_handle(current_config) -> ?hkey_current_config; 397root_to_handle(hkcc) -> ?hkey_current_config; 398root_to_handle(dyn_data) -> ?hkey_dyn_data; 399root_to_handle(hkdd) -> ?hkey_dyn_data; 400root_to_handle(performance_data) -> ?hkey_performance_data; 401root_to_handle(_) -> false. 402 403hkey_to_string(?hkey_classes_root) -> "\\hkey_classes_root"; 404hkey_to_string(?hkey_current_user) -> "\\hkey_current_user"; 405hkey_to_string(?hkey_local_machine) -> "\\hkey_local_machine"; 406hkey_to_string(?hkey_users) -> "\\hkey_users"; 407hkey_to_string(?hkey_performance_data) -> "\\hkey_performance_data"; 408hkey_to_string(?hkey_current_config) -> "\\hkey_current_config"; 409hkey_to_string(?hkey_dyn_data) -> "\\hkey_dyn_data". 410 411open_mode([read|Rest], Result) -> 412 open_mode(Rest, [$r|Result]); 413open_mode([write|Rest], Result) -> 414 open_mode(Rest, [$w|Result]); 415open_mode([], Result) -> 416 Result; 417open_mode(_, _) -> 418 {error, einval}. 419