export.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. * Copyright 2017 Hugh McMaster
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  17. */
  18. #include <stdio.h>
  19. #include "reg.h"
  20. static void write_file(HANDLE hFile, const WCHAR *str)
  21. {
  22. DWORD written;
  23. WriteFile(hFile, str, lstrlenW(str) * sizeof(WCHAR), &written, NULL);
  24. }
  25. static WCHAR *escape_string(WCHAR *str, size_t str_len, size_t *line_len)
  26. {
  27. size_t i, escape_count, pos;
  28. WCHAR *buf;
  29. for (i = 0, escape_count = 0; i < str_len; i++)
  30. {
  31. WCHAR c = str[i];
  32. if (!c) break;
  33. if (c == '\r' || c == '\n' || c == '\\' || c == '"')
  34. escape_count++;
  35. }
  36. buf = malloc((str_len + escape_count + 1) * sizeof(WCHAR));
  37. for (i = 0, pos = 0; i < str_len; i++, pos++)
  38. {
  39. WCHAR c = str[i];
  40. if (!c) break;
  41. switch (c)
  42. {
  43. case '\r':
  44. buf[pos++] = '\\';
  45. buf[pos] = 'r';
  46. break;
  47. case '\n':
  48. buf[pos++] = '\\';
  49. buf[pos] = 'n';
  50. break;
  51. case '\\':
  52. buf[pos++] = '\\';
  53. buf[pos] = '\\';
  54. break;
  55. case '"':
  56. buf[pos++] = '\\';
  57. buf[pos] = '"';
  58. break;
  59. default:
  60. buf[pos] = c;
  61. }
  62. }
  63. buf[pos] = 0;
  64. *line_len = pos;
  65. return buf;
  66. }
  67. static size_t export_value_name(HANDLE hFile, WCHAR *name, size_t len)
  68. {
  69. static const WCHAR *default_name = L"@=";
  70. size_t line_len;
  71. if (name && *name)
  72. {
  73. WCHAR *str = escape_string(name, len, &line_len);
  74. WCHAR *buf = malloc((line_len + 4) * sizeof(WCHAR));
  75. line_len = swprintf(buf, line_len + 4, L"\"%s\"=", str);
  76. write_file(hFile, buf);
  77. free(buf);
  78. free(str);
  79. }
  80. else
  81. {
  82. line_len = lstrlenW(default_name);
  83. write_file(hFile, default_name);
  84. }
  85. return line_len;
  86. }
  87. static void export_string_data(WCHAR **buf, WCHAR *data, size_t size)
  88. {
  89. size_t len = 0, line_len;
  90. WCHAR *str;
  91. if (size)
  92. len = size / sizeof(WCHAR) - 1;
  93. str = escape_string(data, len, &line_len);
  94. *buf = malloc((line_len + 3) * sizeof(WCHAR));
  95. swprintf(*buf, line_len + 3, L"\"%s\"", str);
  96. free(str);
  97. }
  98. static void export_dword_data(WCHAR **buf, DWORD *data)
  99. {
  100. *buf = malloc(15 * sizeof(WCHAR));
  101. swprintf(*buf, 15, L"dword:%08x", *data);
  102. }
  103. static size_t export_hex_data_type(HANDLE hFile, DWORD type)
  104. {
  105. static const WCHAR *hex = L"hex:";
  106. size_t line_len;
  107. if (type == REG_BINARY)
  108. {
  109. line_len = lstrlenW(hex);
  110. write_file(hFile, hex);
  111. }
  112. else
  113. {
  114. WCHAR *buf = malloc(15 * sizeof(WCHAR));
  115. line_len = swprintf(buf, 15, L"hex(%x):", type);
  116. write_file(hFile, buf);
  117. free(buf);
  118. }
  119. return line_len;
  120. }
  121. #define MAX_HEX_CHARS 77
  122. static void export_hex_data(HANDLE hFile, WCHAR **buf, DWORD type,
  123. DWORD line_len, void *data, DWORD size)
  124. {
  125. size_t num_commas, i, pos;
  126. line_len += export_hex_data_type(hFile, type);
  127. if (!size) return;
  128. num_commas = size - 1;
  129. *buf = malloc(size * 3 * sizeof(WCHAR));
  130. for (i = 0, pos = 0; i < size; i++)
  131. {
  132. pos += swprintf(*buf + pos, 3, L"%02x", ((BYTE *)data)[i]);
  133. if (i == num_commas) break;
  134. (*buf)[pos++] = ',';
  135. (*buf)[pos] = 0;
  136. line_len += 3;
  137. if (line_len >= MAX_HEX_CHARS)
  138. {
  139. write_file(hFile, *buf);
  140. write_file(hFile, L"\\\r\n ");
  141. line_len = 2;
  142. pos = 0;
  143. }
  144. }
  145. }
  146. static void export_newline(HANDLE hFile)
  147. {
  148. static const WCHAR *newline = L"\r\n";
  149. write_file(hFile, newline);
  150. }
  151. static void export_data(HANDLE hFile, WCHAR *value_name, DWORD value_len,
  152. DWORD type, void *data, size_t size)
  153. {
  154. WCHAR *buf = NULL;
  155. size_t line_len = export_value_name(hFile, value_name, value_len);
  156. switch (type)
  157. {
  158. case REG_SZ:
  159. export_string_data(&buf, data, size);
  160. break;
  161. case REG_DWORD:
  162. if (size)
  163. {
  164. export_dword_data(&buf, data);
  165. break;
  166. }
  167. /* fall through */
  168. case REG_NONE:
  169. case REG_EXPAND_SZ:
  170. case REG_BINARY:
  171. case REG_MULTI_SZ:
  172. default:
  173. export_hex_data(hFile, &buf, type, line_len, data, size);
  174. break;
  175. }
  176. if (size || type == REG_SZ)
  177. {
  178. write_file(hFile, buf);
  179. free(buf);
  180. }
  181. export_newline(hFile);
  182. }
  183. static void export_key_name(HANDLE hFile, WCHAR *name)
  184. {
  185. WCHAR *buf;
  186. buf = malloc((lstrlenW(name) + 7) * sizeof(WCHAR));
  187. swprintf(buf, lstrlenW(name) + 7, L"\r\n[%s]\r\n", name);
  188. write_file(hFile, buf);
  189. free(buf);
  190. }
  191. static int export_registry_data(HANDLE hFile, HKEY hkey, WCHAR *path, REGSAM sam)
  192. {
  193. LONG rc;
  194. DWORD max_value_len = 256, value_len;
  195. DWORD max_data_bytes = 2048, data_size;
  196. DWORD subkey_len;
  197. DWORD i, type, path_len;
  198. WCHAR *value_name, *subkey_name, *subkey_path;
  199. BYTE *data;
  200. HKEY subkey;
  201. export_key_name(hFile, path);
  202. value_name = malloc(max_value_len * sizeof(WCHAR));
  203. data = malloc(max_data_bytes);
  204. i = 0;
  205. for (;;)
  206. {
  207. value_len = max_value_len;
  208. data_size = max_data_bytes;
  209. rc = RegEnumValueW(hkey, i, value_name, &value_len, NULL, &type, data, &data_size);
  210. if (rc == ERROR_SUCCESS)
  211. {
  212. export_data(hFile, value_name, value_len, type, data, data_size);
  213. i++;
  214. }
  215. else if (rc == ERROR_MORE_DATA)
  216. {
  217. if (data_size > max_data_bytes)
  218. {
  219. max_data_bytes = data_size;
  220. data = realloc(data, max_data_bytes);
  221. }
  222. else
  223. {
  224. max_value_len *= 2;
  225. value_name = realloc(value_name, max_value_len * sizeof(WCHAR));
  226. }
  227. }
  228. else break;
  229. }
  230. free(data);
  231. free(value_name);
  232. subkey_name = malloc(MAX_SUBKEY_LEN * sizeof(WCHAR));
  233. path_len = lstrlenW(path);
  234. i = 0;
  235. for (;;)
  236. {
  237. subkey_len = MAX_SUBKEY_LEN;
  238. rc = RegEnumKeyExW(hkey, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL);
  239. if (rc == ERROR_SUCCESS)
  240. {
  241. subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len);
  242. if (!RegOpenKeyExW(hkey, subkey_name, 0, KEY_READ|sam, &subkey))
  243. {
  244. export_registry_data(hFile, subkey, subkey_path, sam);
  245. RegCloseKey(subkey);
  246. }
  247. free(subkey_path);
  248. i++;
  249. }
  250. else break;
  251. }
  252. free(subkey_name);
  253. return 0;
  254. }
  255. static void export_file_header(HANDLE hFile)
  256. {
  257. static const WCHAR header[] = L"\xFEFFWindows Registry Editor Version 5.00\r\n";
  258. write_file(hFile, header);
  259. }
  260. static HANDLE create_file(const WCHAR *filename, DWORD action)
  261. {
  262. return CreateFileW(filename, GENERIC_WRITE, 0, NULL, action, FILE_ATTRIBUTE_NORMAL, NULL);
  263. }
  264. static HANDLE get_file_handle(WCHAR *filename, BOOL overwrite_file)
  265. {
  266. HANDLE hFile = create_file(filename, overwrite_file ? CREATE_ALWAYS : CREATE_NEW);
  267. if (hFile == INVALID_HANDLE_VALUE)
  268. {
  269. DWORD error = GetLastError();
  270. if (error == ERROR_FILE_EXISTS)
  271. {
  272. if (!ask_confirm(STRING_OVERWRITE_FILE, filename))
  273. {
  274. output_message(STRING_CANCELLED);
  275. exit(0);
  276. }
  277. hFile = create_file(filename, CREATE_ALWAYS);
  278. }
  279. else
  280. {
  281. WCHAR *str;
  282. FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
  283. FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (WCHAR *)&str, 0, NULL);
  284. output_writeconsole(str, lstrlenW(str));
  285. LocalFree(str);
  286. exit(1);
  287. }
  288. }
  289. return hFile;
  290. }
  291. int reg_export(int argc, WCHAR *argvW[])
  292. {
  293. HKEY root, hkey;
  294. WCHAR *path, *key_name;
  295. BOOL overwrite_file = FALSE;
  296. REGSAM sam = 0;
  297. HANDLE hFile;
  298. int i, ret;
  299. if (argc < 4) goto invalid;
  300. if (!parse_registry_key(argvW[2], &root, &path))
  301. return 1;
  302. for (i = 4; i < argc; i++)
  303. {
  304. WCHAR *str;
  305. if (argvW[i][0] != '/' && argvW[i][0] != '-')
  306. goto invalid;
  307. str = &argvW[i][1];
  308. if (is_char(*str, 'y') && !str[1])
  309. overwrite_file = TRUE;
  310. else if (!lstrcmpiW(str, L"reg:32"))
  311. {
  312. if (sam & KEY_WOW64_32KEY) goto invalid;
  313. sam |= KEY_WOW64_32KEY;
  314. continue;
  315. }
  316. else if (!lstrcmpiW(str, L"reg:64"))
  317. {
  318. if (sam & KEY_WOW64_64KEY) goto invalid;
  319. sam |= KEY_WOW64_64KEY;
  320. continue;
  321. }
  322. else
  323. goto invalid;
  324. }
  325. if (sam == (KEY_WOW64_32KEY|KEY_WOW64_64KEY))
  326. goto invalid;
  327. if (RegOpenKeyExW(root, path, 0, KEY_READ|sam, &hkey))
  328. {
  329. output_message(STRING_KEY_NONEXIST);
  330. return 1;
  331. }
  332. key_name = get_long_key(root, path);
  333. hFile = get_file_handle(argvW[3], overwrite_file);
  334. export_file_header(hFile);
  335. ret = export_registry_data(hFile, hkey, key_name, sam);
  336. export_newline(hFile);
  337. CloseHandle(hFile);
  338. RegCloseKey(hkey);
  339. return ret;
  340. invalid:
  341. output_message(STRING_INVALID_SYNTAX);
  342. output_message(STRING_FUNC_HELP, wcsupr(argvW[1]));
  343. return 1;
  344. }