1 /* 2 * Copyright 2008 Andrew Riedi 3 * Copyright 2016-2017, 2021 Hugh McMaster 4 * 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. 9 * 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. 14 * 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 18 */ 19 20 #include <stdio.h> 21 #include "reg.h" 22 #include <wine/debug.h> 23 24 WINE_DEFAULT_DEBUG_CHANNEL(reg); 25 26 static const struct 27 { 28 HKEY key; 29 const WCHAR *short_name; 30 const WCHAR *long_name; 31 } 32 root_rels[] = 33 { 34 {HKEY_LOCAL_MACHINE, L"HKLM", L"HKEY_LOCAL_MACHINE"}, 35 {HKEY_CURRENT_USER, L"HKCU", L"HKEY_CURRENT_USER"}, 36 {HKEY_CLASSES_ROOT, L"HKCR", L"HKEY_CLASSES_ROOT"}, 37 {HKEY_USERS, L"HKU", L"HKEY_USERS"}, 38 {HKEY_CURRENT_CONFIG, L"HKCC", L"HKEY_CURRENT_CONFIG"}, 39 }; 40 41 const struct reg_type_rels type_rels[] = 42 { 43 {REG_NONE, L"REG_NONE"}, 44 {REG_SZ, L"REG_SZ"}, 45 {REG_EXPAND_SZ, L"REG_EXPAND_SZ"}, 46 {REG_BINARY, L"REG_BINARY"}, 47 {REG_DWORD, L"REG_DWORD"}, 48 {REG_DWORD_LITTLE_ENDIAN, L"REG_DWORD_LITTLE_ENDIAN"}, 49 {REG_DWORD_BIG_ENDIAN, L"REG_DWORD_BIG_ENDIAN"}, 50 {REG_MULTI_SZ, L"REG_MULTI_SZ"}, 51 }; 52 53 void output_writeconsole(const WCHAR *str, DWORD wlen) 54 { 55 DWORD count, ret; 56 57 ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL); 58 if (!ret) 59 { 60 DWORD len; 61 char *msgA; 62 63 /* On Windows WriteConsoleW() fails if the output is redirected. So fall 64 * back to WriteFile(), assuming the console encoding is still the right 65 * one in that case. 66 */ 67 len = WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, NULL, 0, NULL, NULL); 68 msgA = malloc(len); 69 70 WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, msgA, len, NULL, NULL); 71 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE); 72 free(msgA); 73 } 74 } 75 76 static void output_formatstring(const WCHAR *fmt, va_list va_args) 77 { 78 WCHAR *str; 79 DWORD len; 80 81 len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, 82 fmt, 0, 0, (WCHAR *)&str, 0, &va_args); 83 if (len == 0 && GetLastError() != NO_ERROR) 84 { 85 WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt)); 86 return; 87 } 88 output_writeconsole(str, len); 89 LocalFree(str); 90 } 91 92 void WINAPIV output_message(unsigned int id, ...) 93 { 94 WCHAR *fmt = NULL; 95 int len; 96 va_list va_args; 97 98 if (!(len = LoadStringW(GetModuleHandleW(NULL), id, (WCHAR *)&fmt, 0))) 99 { 100 WINE_FIXME("LoadString failed with %d\n", GetLastError()); 101 return; 102 } 103 104 len++; 105 fmt = malloc(len * sizeof(WCHAR)); 106 if (!fmt) return; 107 108 LoadStringW(GetModuleHandleW(NULL), id, fmt, len); 109 110 va_start(va_args, id); 111 output_formatstring(fmt, va_args); 112 va_end(va_args); 113 114 free(fmt); 115 } 116 117 void WINAPIV output_string(const WCHAR *fmt, ...) 118 { 119 va_list va_args; 120 121 va_start(va_args, fmt); 122 output_formatstring(fmt, va_args); 123 va_end(va_args); 124 } 125 126 /* ask_confirm() adapted from programs/cmd/builtins.c */ 127 BOOL ask_confirm(unsigned int msgid, WCHAR *reg_info) 128 { 129 HMODULE hmod; 130 WCHAR Ybuffer[4]; 131 WCHAR Nbuffer[4]; 132 WCHAR defval[32]; 133 WCHAR answer[MAX_PATH]; 134 WCHAR *str; 135 DWORD count; 136 137 hmod = GetModuleHandleW(NULL); 138 LoadStringW(hmod, STRING_YES, Ybuffer, ARRAY_SIZE(Ybuffer)); 139 LoadStringW(hmod, STRING_NO, Nbuffer, ARRAY_SIZE(Nbuffer)); 140 LoadStringW(hmod, STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval)); 141 142 str = (reg_info && *reg_info) ? reg_info : defval; 143 144 while (1) 145 { 146 output_message(msgid, str); 147 output_message(STRING_YESNO); 148 ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count, NULL); 149 answer[0] = towupper(answer[0]); 150 if (answer[0] == Ybuffer[0]) 151 return TRUE; 152 if (answer[0] == Nbuffer[0]) 153 return FALSE; 154 } 155 } 156 157 static inline BOOL path_rootname_cmp(const WCHAR *input_path, const WCHAR *rootkey_name) 158 { 159 DWORD length = lstrlenW(rootkey_name); 160 161 return (!_wcsnicmp(input_path, rootkey_name, length) && 162 (input_path[length] == 0 || input_path[length] == '\\')); 163 } 164 165 HKEY path_get_rootkey(const WCHAR *path) 166 { 167 DWORD i; 168 169 for (i = 0; i < ARRAY_SIZE(root_rels); i++) 170 { 171 if (path_rootname_cmp(path, root_rels[i].short_name) || 172 path_rootname_cmp(path, root_rels[i].long_name)) 173 return root_rels[i].key; 174 } 175 176 return NULL; 177 } 178 179 static BOOL sane_path(const WCHAR *key) 180 { 181 unsigned int i = lstrlenW(key); 182 183 if (i < 3 || (key[i - 1] == '\\' && key[i - 2] == '\\')) 184 { 185 output_message(STRING_INVALID_KEY); 186 return FALSE; 187 } 188 189 if (key[0] == '\\' && key[1] == '\\' && key[2] != '\\') 190 { 191 output_message(STRING_NO_REMOTE); 192 return FALSE; 193 } 194 195 return TRUE; 196 } 197 198 WCHAR *build_subkey_path(WCHAR *path, DWORD path_len, WCHAR *subkey_name, DWORD subkey_len) 199 { 200 WCHAR *subkey_path; 201 202 subkey_path = malloc((path_len + subkey_len + 2) * sizeof(WCHAR)); 203 swprintf(subkey_path, L"%s\\%s", path, subkey_name); 204 205 return subkey_path; 206 } 207 208 WCHAR *get_long_key(HKEY root, WCHAR *path) 209 { 210 int i, len; 211 WCHAR *long_key; 212 213 for (i = 0; i < ARRAY_SIZE(root_rels); i++) 214 { 215 if (root == root_rels[i].key) 216 break; 217 } 218 219 len = lstrlenW(root_rels[i].long_name); 220 221 if (!path) 222 { 223 long_key = malloc((len + 1) * sizeof(WCHAR)); 224 lstrcpyW(long_key, root_rels[i].long_name); 225 return long_key; 226 } 227 228 len += lstrlenW(path) + 1; /* add one for the concatenating backslash */ 229 long_key = malloc((len + 1) * sizeof(WCHAR)); 230 swprintf(long_key, L"%s\\%s", root_rels[i].long_name, path); 231 return long_key; 232 } 233 234 BOOL parse_registry_key(const WCHAR *key, HKEY *root, WCHAR **path) 235 { 236 WCHAR *p; 237 238 if (!sane_path(key)) 239 return FALSE; 240 241 *root = path_get_rootkey(key); 242 if (!*root) 243 { 244 output_message(STRING_INVALID_SYSTEM_KEY); 245 return FALSE; 246 } 247 248 *path = wcschr(key, '\\'); 249 250 if (!*path) 251 return TRUE; 252 253 (*path)++; 254 255 if (!**path) 256 { 257 output_message(STRING_INVALID_SYSTEM_KEY); 258 return FALSE; 259 } 260 261 p = *path + lstrlenW(*path) - 1; 262 if (*p == '\\') *p = 0; 263 264 return TRUE; 265 } 266 267 BOOL is_char(const WCHAR s, const WCHAR c) 268 { 269 return (s == c || s == towupper(c)); 270 } 271 272 BOOL is_switch(const WCHAR *s, const WCHAR c) 273 { 274 if (lstrlenW(s) > 2) 275 return FALSE; 276 277 return ((s[0] == '/' || s[0] == '-') && is_char(s[1], c)); 278 } 279 280 static BOOL is_help_switch(const WCHAR *s) 281 { 282 return (is_switch(s, '?') || is_switch(s, 'h')); 283 } 284 285 enum operations { 286 REG_ADD, 287 REG_COPY, 288 REG_DELETE, 289 REG_EXPORT, 290 REG_IMPORT, 291 REG_QUERY, 292 REG_INVALID 293 }; 294 295 static enum operations get_operation(const WCHAR *str, int *op_help) 296 { 297 struct op_info { const WCHAR *op; int id; int help_id; }; 298 299 static const struct op_info op_array[] = 300 { 301 { L"add", REG_ADD, STRING_ADD_USAGE }, 302 { L"copy", REG_COPY, STRING_COPY_USAGE }, 303 { L"delete", REG_DELETE, STRING_DELETE_USAGE }, 304 { L"export", REG_EXPORT, STRING_EXPORT_USAGE }, 305 { L"import", REG_IMPORT, STRING_IMPORT_USAGE }, 306 { L"query", REG_QUERY, STRING_QUERY_USAGE }, 307 { NULL, -1, 0 } 308 }; 309 310 const struct op_info *ptr; 311 312 for (ptr = op_array; ptr->op; ptr++) 313 { 314 if (!lstrcmpiW(str, ptr->op)) 315 { 316 *op_help = ptr->help_id; 317 return ptr->id; 318 } 319 } 320 321 return REG_INVALID; 322 } 323 324 int __cdecl wmain(int argc, WCHAR *argvW[]) 325 { 326 int op, op_help; 327 328 if (argc == 1) 329 { 330 output_message(STRING_INVALID_SYNTAX); 331 output_message(STRING_REG_HELP); 332 return 1; 333 } 334 335 if (is_help_switch(argvW[1])) 336 { 337 output_message(STRING_USAGE); 338 return 0; 339 } 340 341 op = get_operation(argvW[1], &op_help); 342 343 if (op == REG_INVALID) 344 { 345 output_message(STRING_INVALID_OPTION, argvW[1]); 346 output_message(STRING_REG_HELP); 347 return 1; 348 } 349 else if (argc == 2) /* Valid operation, no arguments supplied */ 350 goto invalid; 351 352 if (is_help_switch(argvW[2])) 353 { 354 if (argc > 3) goto invalid; 355 356 output_message(op_help); 357 output_message(STRING_REG_VIEW_USAGE); 358 return 0; 359 } 360 361 if (op == REG_ADD) 362 return reg_add(argc, argvW); 363 364 if (op == REG_COPY) 365 return reg_copy(argc, argvW); 366 367 if (op == REG_DELETE) 368 return reg_delete(argc, argvW); 369 370 if (op == REG_EXPORT) 371 return reg_export(argc, argvW); 372 373 if (op == REG_IMPORT) 374 return reg_import(argc, argvW); 375 376 return reg_query(argc, argvW); 377 378 invalid: 379 output_message(STRING_INVALID_SYNTAX); 380 output_message(STRING_FUNC_HELP, _wcsupr(argvW[1])); 381 return 1; 382 } 383