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_MULTI_SZ
, L
"REG_MULTI_SZ"},
52 void output_writeconsole(const WCHAR
*str
, DWORD wlen
)
56 if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE
), str
, wlen
, &count
, NULL
))
61 /* On Windows WriteConsoleW() fails if the output is redirected. So fall
62 * back to WriteFile() with OEM code page.
64 len
= WideCharToMultiByte(GetOEMCP(), 0, str
, wlen
, NULL
, 0, NULL
, NULL
);
67 WideCharToMultiByte(GetOEMCP(), 0, str
, wlen
, msgA
, len
, NULL
, NULL
);
68 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), msgA
, len
, &count
, FALSE
);
73 static void output_formatstring(const WCHAR
*fmt
, va_list va_args
)
78 len
= FormatMessageW(FORMAT_MESSAGE_FROM_STRING
|FORMAT_MESSAGE_ALLOCATE_BUFFER
,
79 fmt
, 0, 0, (WCHAR
*)&str
, 0, &va_args
);
80 if (len
== 0 && GetLastError() != ERROR_NO_WORK_DONE
)
82 WINE_FIXME("Could not format string: le=%lu, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt
));
85 output_writeconsole(str
, len
);
89 void WINAPIV
output_message(unsigned int id
, ...)
95 if (!(len
= LoadStringW(GetModuleHandleW(NULL
), id
, (WCHAR
*)&fmt
, 0)))
97 WINE_FIXME("LoadString failed with %ld\n", GetLastError());
102 fmt
= malloc(len
* sizeof(WCHAR
));
105 LoadStringW(GetModuleHandleW(NULL
), id
, fmt
, len
);
107 va_start(va_args
, id
);
108 output_formatstring(fmt
, va_args
);
114 void WINAPIV
output_string(const WCHAR
*fmt
, ...)
118 va_start(va_args
, fmt
);
119 output_formatstring(fmt
, va_args
);
123 /* ask_confirm() adapted from programs/cmd/builtins.c */
124 BOOL
ask_confirm(unsigned int msgid
, WCHAR
*reg_info
)
130 WCHAR answer
[MAX_PATH
];
134 hmod
= GetModuleHandleW(NULL
);
135 LoadStringW(hmod
, STRING_YES
, Ybuffer
, ARRAY_SIZE(Ybuffer
));
136 LoadStringW(hmod
, STRING_NO
, Nbuffer
, ARRAY_SIZE(Nbuffer
));
137 LoadStringW(hmod
, STRING_DEFAULT_VALUE
, defval
, ARRAY_SIZE(defval
));
139 str
= (reg_info
&& *reg_info
) ? reg_info
: defval
;
143 output_message(msgid
, str
);
144 output_message(STRING_YESNO
);
145 ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE
), answer
, ARRAY_SIZE(answer
), &count
, NULL
);
146 answer
[0] = towupper(answer
[0]);
147 if (answer
[0] == Ybuffer
[0])
149 if (answer
[0] == Nbuffer
[0])
154 static inline BOOL
path_rootname_cmp(const WCHAR
*input_path
, const WCHAR
*rootkey_name
)
156 DWORD length
= lstrlenW(rootkey_name
);
158 return (!wcsnicmp(input_path
, rootkey_name
, length
) &&
159 (input_path
[length
] == 0 || input_path
[length
] == '\\'));
162 HKEY
path_get_rootkey(const WCHAR
*path
)
166 for (i
= 0; i
< ARRAY_SIZE(root_rels
); i
++)
168 if (path_rootname_cmp(path
, root_rels
[i
].short_name
) ||
169 path_rootname_cmp(path
, root_rels
[i
].long_name
))
170 return root_rels
[i
].key
;
176 static BOOL
sane_path(const WCHAR
*key
)
178 unsigned int i
= lstrlenW(key
);
180 if (i
< 3 || (key
[i
- 1] == '\\' && key
[i
- 2] == '\\'))
182 output_message(STRING_INVALID_KEY
);
186 if (key
[0] == '\\' && key
[1] == '\\' && key
[2] != '\\')
188 output_message(STRING_NO_REMOTE
);
195 WCHAR
*build_subkey_path(WCHAR
*path
, DWORD path_len
, WCHAR
*subkey_name
, DWORD subkey_len
)
199 subkey_path
= malloc((path_len
+ subkey_len
+ 2) * sizeof(WCHAR
));
200 swprintf(subkey_path
, path_len
+ subkey_len
+ 2, L
"%s\\%s", path
, subkey_name
);
205 WCHAR
*get_long_key(HKEY root
, WCHAR
*path
)
210 for (i
= 0; i
< ARRAY_SIZE(root_rels
); i
++)
212 if (root
== root_rels
[i
].key
)
216 len
= lstrlenW(root_rels
[i
].long_name
);
220 long_key
= malloc((len
+ 1) * sizeof(WCHAR
));
221 lstrcpyW(long_key
, root_rels
[i
].long_name
);
225 len
+= lstrlenW(path
) + 1; /* add one for the concatenating backslash */
226 long_key
= malloc((len
+ 1) * sizeof(WCHAR
));
227 swprintf(long_key
, len
+ 1, L
"%s\\%s", root_rels
[i
].long_name
, path
);
231 BOOL
parse_registry_key(const WCHAR
*key
, HKEY
*root
, WCHAR
**path
)
238 *root
= path_get_rootkey(key
);
241 output_message(STRING_INVALID_SYSTEM_KEY
);
245 *path
= wcschr(key
, '\\');
254 output_message(STRING_INVALID_SYSTEM_KEY
);
258 p
= *path
+ lstrlenW(*path
) - 1;
259 if (*p
== '\\') *p
= 0;
264 BOOL
is_char(const WCHAR s
, const WCHAR c
)
266 return (s
== c
|| s
== towupper(c
));
269 BOOL
is_switch(const WCHAR
*s
, const WCHAR c
)
274 return ((s
[0] == '/' || s
[0] == '-') && is_char(s
[1], c
));
277 static BOOL
is_help_switch(const WCHAR
*s
)
279 return (is_switch(s
, '?') || is_switch(s
, 'h'));
292 static enum operations
get_operation(const WCHAR
*str
, int *op_help
)
294 struct op_info
{ const WCHAR
*op
; int id
; int help_id
; };
296 static const struct op_info op_array
[] =
298 { L
"add", REG_ADD
, STRING_ADD_USAGE
},
299 { L
"copy", REG_COPY
, STRING_COPY_USAGE
},
300 { L
"delete", REG_DELETE
, STRING_DELETE_USAGE
},
301 { L
"export", REG_EXPORT
, STRING_EXPORT_USAGE
},
302 { L
"import", REG_IMPORT
, STRING_IMPORT_USAGE
},
303 { L
"query", REG_QUERY
, STRING_QUERY_USAGE
},
307 const struct op_info
*ptr
;
309 for (ptr
= op_array
; ptr
->op
; ptr
++)
311 if (!lstrcmpiW(str
, ptr
->op
))
313 *op_help
= ptr
->help_id
;
321 int __cdecl
wmain(int argc
, WCHAR
*argvW
[])
327 output_message(STRING_INVALID_SYNTAX
);
328 output_message(STRING_REG_HELP
);
332 if (is_help_switch(argvW
[1]))
334 output_message(STRING_USAGE
);
338 op
= get_operation(argvW
[1], &op_help
);
340 if (op
== REG_INVALID
)
342 output_message(STRING_INVALID_OPTION
, argvW
[1]);
343 output_message(STRING_REG_HELP
);
346 else if (argc
== 2) /* Valid operation, no arguments supplied */
349 if (is_help_switch(argvW
[2]))
351 if (argc
> 3) goto invalid
;
353 output_message(op_help
);
354 output_message(STRING_REG_VIEW_USAGE
);
359 return reg_add(argc
, argvW
);
362 return reg_copy(argc
, argvW
);
364 if (op
== REG_DELETE
)
365 return reg_delete(argc
, argvW
);
367 if (op
== REG_EXPORT
)
368 return reg_export(argc
, argvW
);
370 if (op
== REG_IMPORT
)
371 return reg_import(argc
, argvW
);
373 return reg_query(argc
, argvW
);
376 output_message(STRING_INVALID_SYNTAX
);
377 output_message(STRING_FUNC_HELP
, wcsupr(argvW
[1]));