winprefs v0.3.2
A registry exporter for programmers.
Loading...
Searching...
No Matches
registry.c
1#include "registry.h"
2#include "constants.h"
3#include "git.h"
4#include "io.h"
5#include "io_default_writer.h"
6#include "reg_code.h"
7#include "reg_command.h"
8
9static inline bool create_dir_wrapper(wchar_t *folder) {
10 if (!CreateDirectory(folder, nullptr)) {
11 DWORD err = GetLastError();
12 if (err != ERROR_ALREADY_EXISTS && err != ERROR_ACCESS_DENIED) {
13 return false;
14 }
15 }
16 return true;
17}
18
19static inline bool create_dir_recursive(wchar_t *path) {
20 wchar_t folder[MAX_PATH];
21 wchar_t *end;
22 wmemset(folder, L'\0', MAX_PATH);
23 end = wcschr(path, L'\\');
24 while (end != nullptr) {
25 wcsncpy(folder, path, (size_t)(end - path + 1));
26 if (!create_dir_wrapper(folder)) {
27 return false;
28 }
29 end = wcschr(++end, L'\\');
30 }
31 if (!create_dir_wrapper(path)) {
32 return false;
33 }
34 return true;
35}
36
37DLL_EXPORT bool save_preferences(bool commit,
38 const wchar_t *deploy_key,
39 const wchar_t *output_dir,
40 const wchar_t *output_file,
41 int max_depth,
42 HKEY hk,
43 const wchar_t *specified_path,
44 enum OUTPUT_FORMAT format,
45 writer_t *writer) {
46 bool using_default_writer, writer_was_setup, writer_was_torn_down;
47 using_default_writer = writer_was_setup = writer_was_torn_down = false;
48 if (!writer || !writer->write_output) {
49 debug_print(L"Using default writer.");
50 writer = get_default_writer();
51 using_default_writer = true;
52 }
53 bool ret = true;
54 wchar_t full_output_dir[MAX_PATH];
55 bool writing_to_stdout = !wcscmp(L"-", output_file);
56 if (!_wfullpath(full_output_dir, output_dir, MAX_PATH)) {
57 goto fail;
58 }
59 if (!writing_to_stdout) {
60 debug_print(L"Output directory: %ls\n", full_output_dir);
61 } else {
62 debug_print(L"Writing to standard output.\n");
63 }
64 if (!writing_to_stdout && !create_dir_recursive(full_output_dir)) {
65 goto fail;
66 }
67 PathAppend(full_output_dir, output_file);
68 full_output_dir[MAX_PATH - 1] = L'\0';
69 if (writer->setup && !writer->setup(writer, writing_to_stdout, full_output_dir)) {
70 goto fail;
71 }
72 writer_was_setup = true;
73 const wchar_t *prior_stem = hk == HKEY_CLASSES_ROOT ? L"HKCR" :
74 hk == HKEY_CURRENT_CONFIG ? L"HKCC" :
75 hk == HKEY_CURRENT_USER ? L"HKCU" :
76 hk == HKEY_LOCAL_MACHINE ? L"HKLM" :
77 hk == HKEY_USERS ? L"HKU" :
78 hk == HKEY_DYN_DATA ? L"HKDD" :
79 specified_path;
80 if (format == OUTPUT_FORMAT_C) {
81 DWORD written;
82 writer->write_output(writer, C_PREAMBLE, (DWORD)SIZEOF_C_PREAMBLE, &written);
83 }
84 ret = write_key_filtered_recursive(hk, nullptr, max_depth, 0, prior_stem, format, writer);
85 if (writer_was_setup && writer->teardown) {
86 writer->teardown(writer);
87 writer_was_torn_down = true;
88 }
89 if (ret && commit && !writing_to_stdout) {
90 git_commit(output_dir, deploy_key);
91 }
92 goto cleanup;
93fail:
94 ret = false;
95 cleanup:
96 if (!writer_was_torn_down && writer_was_setup && writer->teardown) {
97 writer->teardown(writer);
98 }
99 if (using_default_writer) {
100 free(writer);
101 }
102 return ret;
103}
104
105DLL_EXPORT bool export_single_value(HKEY top_key,
106 const wchar_t *reg_path,
107 enum OUTPUT_FORMAT format,
108 writer_t *writer) {
109 bool using_default_writer, writer_was_setup;
110 using_default_writer = writer_was_setup = false;
111 if (!writer || !writer->write_output) {
112 debug_print(L"Using default writer.");
113 writer = get_default_writer();
114 using_default_writer = true;
115 }
116 bool ret = true;
117 wchar_t *m_reg_path = nullptr;
118 wchar_t *value_name = nullptr;
119 char *data = nullptr;
120 if (!reg_path) {
121 errno = EINVAL;
122 goto fail;
123 }
124 size_t reg_path_len = wcslen(reg_path) + 1;
125 m_reg_path = calloc(reg_path_len, WL);
126 if (!m_reg_path) { // LCOV_EXCL_START
127 goto fail;
128 } // LCOV_EXCL_STOP
129 wmemcpy(m_reg_path, reg_path, reg_path_len);
130 wchar_t *first_backslash = wcschr(m_reg_path, L'\\');
131 if (!first_backslash) {
132 errno = EINVAL;
133 goto fail;
134 }
135 wchar_t *subkey = first_backslash + 1;
136 HKEY starting_key = HKEY_CURRENT_USER;
137 wchar_t *last_backslash = wcsrchr(m_reg_path, '\\');
138 wchar_t *value_name_p = last_backslash + 1;
139 size_t value_name_len = wcslen(value_name_p) + 1;
140 value_name = calloc(value_name_len, WL);
141 if (!value_name) { // LCOV_EXCL_START
142 goto fail;
143 } // LCOV_EXCL_STOP
144 wmemset(value_name, L'\0', value_name_len);
145 wmemcpy(value_name, value_name_p, value_name_len);
146 *last_backslash = L'\0';
147 if (RegOpenKeyEx(top_key, subkey, 0, KEY_READ, &starting_key) != ERROR_SUCCESS) {
148 debug_print(L"Invalid subkey: '%ls'.\n", subkey);
149 goto fail;
150 }
151 size_t buf_size = 8192;
152 data = malloc(buf_size);
153 if (!data) { // LCOV_EXCL_START
154 goto fail;
155 } // LCOV_EXCL_STOP
156 DWORD reg_type = REG_NONE;
157 LSTATUS reg_query_value_ret = RegQueryValueEx(
158 starting_key, value_name, nullptr, &reg_type, (LPBYTE)data, (LPDWORD)&buf_size);
159 if (reg_query_value_ret == ERROR_MORE_DATA) {
160 debug_print(L"Value too large (%ls\\%ls).\n", subkey, value_name);
161 goto fail;
162 }
163 if (reg_query_value_ret != ERROR_SUCCESS) {
164 debug_print(L"Invalid value name '%ls'.\n", value_name);
165 goto fail;
166 }
167 if (writer->setup && !writer->setup(writer, true, nullptr)) {
168 goto fail;
169 }
170 writer_was_setup = true;
171 switch (format) {
172 case OUTPUT_FORMAT_REG:
173 if (!do_write_reg_command(writer, reg_path, value_name, data, buf_size, reg_type)) {
174 goto fail;
175 }
176 break;
177 case OUTPUT_FORMAT_C:
178 if (!do_write_c_reg_code(writer, reg_path, value_name, data, buf_size, reg_type)) {
179 goto fail;
180 }
181 break;
182 case OUTPUT_FORMAT_C_SHARP:
183 if (!do_write_c_sharp_reg_code(writer, reg_path, value_name, data, buf_size, reg_type)) {
184 goto fail;
185 }
186 break;
187 case OUTPUT_FORMAT_POWERSHELL:
188 if (!do_write_powershell_reg_code(writer, reg_path, value_name, data, buf_size, reg_type)) {
189 goto fail;
190 }
191 break;
192 default:
193 errno = EINVAL;
194 goto fail;
195 }
196 goto cleanup;
197fail:
198 ret = false;
199cleanup:
200 if (writer_was_setup && writer->teardown) {
201 writer->teardown(writer);
202 }
203 if (using_default_writer) {
204 free(writer);
205 }
206 free(m_reg_path);
207 free(value_name);
208 free(data);
209 return ret;
210}