2 * Copyright 2008 Andrew Riedi
3 * Copyright 2016-2017, 2021 Hugh McMaster
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <wine/debug.h>
23 WINE_DEFAULT_DEBUG_CHANNEL(reg
);
28 const WCHAR
*short_name
;
29 const WCHAR
*long_name
;
33 {HKEY_LOCAL_MACHINE
, L
"HKLM", L
"HKEY_LOCAL_MACHINE"},
34 {HKEY_CURRENT_USER
, L
"HKCU", L
"HKEY_CURRENT_USER"},
35 {HKEY_CLASSES_ROOT
, L
"HKCR", L
"HKEY_CLASSES_ROOT"},
36 {HKEY_USERS
, L
"HKU", L
"HKEY_USERS"},
37 {HKEY_CURRENT_CONFIG
, L
"HKCC", L
"HKEY_CURRENT_CONFIG"},
40 const struct reg_type_rels type_rels
[] =
42 {REG_NONE
, L
"REG_NONE"},
44 {REG_EXPAND_SZ
, L
"REG_EXPAND_SZ"},
45 {REG_BINARY
, L
"REG_BINARY"},
46 {REG_DWORD
, L
"REG_DWORD"},
47 {REG_DWORD_LITTLE_ENDIAN
, L
"REG_DWORD_LITTLE_ENDIAN"},
48 {REG_DWORD_BIG_ENDIAN
, L
"REG_DWORD_BIG_ENDIAN"},
49 {REG_QWORD
, L
"REG_QWORD"},
50 {REG_MULTI_SZ
, L
"REG_MULTI_SZ"},
53 void output_writeconsole(const WCHAR
*str
, DWORD wlen
)
57 if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE
), str
, wlen
, &count
, NULL
))
62 /* On Windows WriteConsoleW() fails if the output is redirected. So fall
63 * back to WriteFile() with OEM code page.
65 len
= WideCharToMultiByte(GetOEMCP(), 0, str
, wlen
, NULL
, 0, NULL
, NULL
);
68 WideCharToMultiByte(GetOEMCP(), 0, str
, wlen
, msgA
, len
, NULL
, NULL
);
69 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), msgA
, len
, &count
, FALSE
);
74 static void output_formatstring(const WCHAR
*fmt
, va_list va_args
)
79 len
= FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ALLOCATE_BUFFER
,
80 fmt
, 0, 0, (WCHAR
*)&str
, 0, &va_args
);
81 if (len
== 0 && GetLastError() != ERROR_NO_WORK_DONE
)
83 WINE_FIXME("Could not format string: le=%lu, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt
));
86 output_writeconsole(str
, len
);
90 void WINAPIV
output_message(unsigned int id
, ...)
96 if (!(len
= LoadStringW(GetModuleHandleW(NULL
), id
, (WCHAR
*)&fmt
, 0)))
98 WINE_FIXME("LoadString failed with %ld\n", GetLastError());
103 fmt
= malloc(len
* sizeof(WCHAR
));
106 LoadStringW(GetModuleHandleW(NULL
), id
, fmt
, len
);
108 va_start(va_args
, id
);
109 output_formatstring(fmt
, va_args
);
115 void WINAPIV
output_string(const WCHAR
*fmt
, ...)
119 va_start(va_args
, fmt
);
120 output_formatstring(fmt
, va_args
);
124 /* ask_confirm() adapted from programs/cmd/builtins.c */
125 BOOL
ask_confirm(unsigned int msgid
, WCHAR
*reg_info
)
131 WCHAR answer
[MAX_PATH
];
135 hmod
= GetModuleHandleW(NULL
);
136 LoadStringW(hmod
, STRING_YES
, Ybuffer
, ARRAY_SIZE(Ybuffer
));
137 LoadStringW(hmod
, STRING_NO
, Nbuffer
, ARRAY_SIZE(Nbuffer
));
138 LoadStringW(hmod
, STRING_DEFAULT_VALUE
, defval
, ARRAY_SIZE(defval
));
140 str
= (reg_info
&& *reg_info
) ? reg_info
: defval
;
144 output_message(msgid
, str
);
145 output_message(STRING_YESNO
);
146 ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE
), answer
, ARRAY_SIZE(answer
), &count
, NULL
);
147 answer
[0] = towupper(answer
[0]);
148 if (answer
[0] == Ybuffer
[0])
150 if (answer
[0] == Nbuffer
[0])
155 static inline BOOL
path_rootname_cmp(const WCHAR
*input_path
, const WCHAR
*rootkey_name
)
157 DWORD length
= lstrlenW(rootkey_name
);
159 return (!wcsnicmp(input_path
, rootkey_name
, length
) &&
160 (input_path
[length
] == 0 || input_path
[length
] == '\\'));
163 HKEY
path_get_rootkey(const WCHAR
*path
)
167 for (i
= 0; i
< ARRAY_SIZE(root_rels
); i
++)
169 if (path_rootname_cmp(path
, root_rels
[i
].short_name
) ||
170 path_rootname_cmp(path
, root_rels
[i
].long_name
))
171 return root_rels
[i
].key
;
177 static BOOL
sane_path(const WCHAR
*key
)
179 unsigned int i
= lstrlenW(key
);
181 if (i
< 3 || (key
[i
- 1] == '\\' && key
[i
- 2] == '\\'))
183 output_message(STRING_INVALID_KEY
);
187 if (key
[0] == '\\' && key
[1] == '\\' && key
[2] != '\\')
189 output_message(STRING_NO_REMOTE
);
196 WCHAR
*build_subkey_path(WCHAR
*path
, DWORD path_len
, WCHAR
*subkey_name
, DWORD subkey_len
)
200 subkey_path
= malloc((path_len
+ subkey_len
+ 2) * sizeof(WCHAR
));
201 swprintf(subkey_path
, path_len
+ subkey_len
+ 2, L
"%s\\%s", path
, subkey_name
);
206 WCHAR
*get_long_key(HKEY root
, WCHAR
*path
)
211 for (i
= 0; i
< ARRAY_SIZE(root_rels
); i
++)
213 if (root
== root_rels
[i
].key
)
217 len
= lstrlenW(root_rels
[i
].long_name
);
221 long_key
= malloc((len
+ 1) * sizeof(WCHAR
));
222 lstrcpyW(long_key
, root_rels
[i
].long_name
);
226 len
+= lstrlenW(path
) + 1; /* add one for the concatenating backslash */
227 long_key
= malloc((len
+ 1) * sizeof(WCHAR
));
228 swprintf(long_key
, len
+ 1, L
"%s\\%s", root_rels
[i
].long_name
, path
);
232 BOOL
parse_registry_key(const WCHAR
*key
, HKEY
*root
, WCHAR
**path
)
239 *root
= path_get_rootkey(key
);
242 output_message(STRING_INVALID_SYSTEM_KEY
);
246 *path
= wcschr(key
, '\\');
255 output_message(STRING_INVALID_SYSTEM_KEY
);
259 p
= *path
+ lstrlenW(*path
) - 1;
260 if (*p
== '\\') *p
= 0;
265 BOOL
is_char(const WCHAR s
, const WCHAR c
)
267 return (s
== c
|| s
== towupper(c
));
270 BOOL
is_switch(const WCHAR
*s
, const WCHAR c
)
275 return ((s
[0] == '/' || s
[0] == '-') && is_char(s
[1], c
));
278 static BOOL
is_help_switch(const WCHAR
*s
)
280 return (is_switch(s
, '?') || is_switch(s
, 'h'));
293 static enum operations
get_operation(const WCHAR
*str
, int *op_help
)
295 struct op_info
{ const WCHAR
*op
; int id
; int help_id
; };
297 static const struct op_info op_array
[] =
299 { L
"add", REG_ADD
, STRING_ADD_USAGE
},
300 { L
"copy", REG_COPY
, STRING_COPY_USAGE
},
301 { L
"delete", REG_DELETE
, STRING_DELETE_USAGE
},
302 { L
"export", REG_EXPORT
, STRING_EXPORT_USAGE
},
303 { L
"import", REG_IMPORT
, STRING_IMPORT_USAGE
},
304 { L
"query", REG_QUERY
, STRING_QUERY_USAGE
},
308 const struct op_info
*ptr
;
310 for (ptr
= op_array
; ptr
->op
; ptr
++)
312 if (!lstrcmpiW(str
, ptr
->op
))
314 *op_help
= ptr
->help_id
;
322 int __cdecl
wmain(int argc
, WCHAR
*argvW
[])
328 output_message(STRING_INVALID_SYNTAX
);
329 output_message(STRING_REG_HELP
);
333 if (is_help_switch(argvW
[1]))
335 output_message(STRING_USAGE
);
339 op
= get_operation(argvW
[1], &op_help
);
341 if (op
== REG_INVALID
)
343 output_message(STRING_INVALID_OPTION
, argvW
[1]);
344 output_message(STRING_REG_HELP
);
347 else if (argc
== 2) /* Valid operation, no arguments supplied */
350 if (is_help_switch(argvW
[2]))
352 if (argc
> 3) goto invalid
;
354 output_message(op_help
);
355 output_message(STRING_REG_VIEW_USAGE
);
360 return reg_add(argc
, argvW
);
363 return reg_copy(argc
, argvW
);
365 if (op
== REG_DELETE
)
366 return reg_delete(argc
, argvW
);
368 if (op
== REG_EXPORT
)
369 return reg_export(argc
, argvW
);
371 if (op
== REG_IMPORT
)
372 return reg_import(argc
, argvW
);
374 return reg_query(argc
, argvW
);
377 output_message(STRING_INVALID_SYNTAX
);
378 output_message(STRING_FUNC_HELP
, wcsupr(argvW
[1]));