/src/core/libntech/libutils/writer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Copyright 2023 Northern.tech AS |
3 | | |
4 | | This file is part of CFEngine 3 - written and maintained by Northern.tech AS. |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify it |
7 | | under the terms of the GNU General Public License as published by the |
8 | | Free Software Foundation; version 3. |
9 | | |
10 | | This program 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 |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program; if not, write to the Free Software |
17 | | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
18 | | |
19 | | To the extent this program is licensed as part of the Enterprise |
20 | | versions of CFEngine, the applicable Commercial Open Source License |
21 | | (COSL) may apply to this file if you as a licensee so wish it. See |
22 | | included file COSL.txt. |
23 | | */ |
24 | | |
25 | | #include <platform.h> |
26 | | #include <writer.h> |
27 | | |
28 | | #include <misc_lib.h> |
29 | | #include <alloc.h> |
30 | | |
31 | | typedef enum |
32 | | { |
33 | | WT_STRING, |
34 | | WT_FILE, |
35 | | } WriterType; |
36 | | |
37 | | typedef struct |
38 | | { |
39 | | char *data; |
40 | | size_t len; /* Does not include trailing zero */ |
41 | | size_t allocated; /* Includes trailing zero */ |
42 | | } StringWriterImpl; |
43 | | |
44 | | struct Writer_ |
45 | | { |
46 | | WriterType type; |
47 | | union |
48 | | { |
49 | | StringWriterImpl string; |
50 | | FILE *file; |
51 | | }; |
52 | | }; |
53 | | |
54 | | /*********************************************************************/ |
55 | | |
56 | | Writer *FileWriter(FILE *file) |
57 | 0 | { |
58 | 0 | Writer *writer = xcalloc(1, sizeof(Writer)); |
59 | |
|
60 | 0 | writer->type = WT_FILE; |
61 | 0 | writer->file = file; |
62 | 0 | return writer; |
63 | 0 | } |
64 | | |
65 | | /*********************************************************************/ |
66 | | |
67 | | Writer *StringWriter(void) |
68 | 0 | { |
69 | 0 | Writer *writer = xcalloc(1, sizeof(Writer)); |
70 | |
|
71 | 0 | writer->type = WT_STRING; |
72 | 0 | writer->string.data = xstrdup(""); |
73 | 0 | writer->string.allocated = 1; |
74 | 0 | writer->string.len = 0; |
75 | 0 | return writer; |
76 | 0 | } |
77 | | |
78 | | /*********************************************************************/ |
79 | | |
80 | | static void StringWriterReallocate(Writer *writer, size_t extra_length) |
81 | 0 | { |
82 | 0 | assert(writer != NULL); |
83 | 0 | writer->string.allocated = MAX(writer->string.allocated * 2, writer->string.len + extra_length + 1); |
84 | 0 | writer->string.data = xrealloc(writer->string.data, writer->string.allocated); |
85 | 0 | } |
86 | | |
87 | | static size_t StringWriterWriteChar(Writer *writer, char c) |
88 | 0 | { |
89 | 0 | assert(writer != NULL); |
90 | 0 | if (writer->string.len + 2 > writer->string.allocated) |
91 | 0 | { |
92 | 0 | StringWriterReallocate(writer, 2); |
93 | 0 | } |
94 | |
|
95 | 0 | writer->string.data[writer->string.len] = c; |
96 | 0 | writer->string.data[writer->string.len + 1] = '\0'; |
97 | 0 | writer->string.len++; |
98 | |
|
99 | 0 | return 1; |
100 | 0 | } |
101 | | |
102 | | static size_t StringWriterWriteLen(Writer *writer, const char *str, size_t len_) |
103 | 0 | { |
104 | 0 | assert(writer != NULL); |
105 | | /* NB: str[:len_] may come from read(), which hasn't '\0'-terminated */ |
106 | 0 | size_t len = strnlen(str, len_); |
107 | |
|
108 | 0 | if (writer->string.len + len + 1 > writer->string.allocated) |
109 | 0 | { |
110 | 0 | StringWriterReallocate(writer, len); |
111 | 0 | } |
112 | |
|
113 | 0 | memcpy(writer->string.data + writer->string.len, str, len); |
114 | 0 | writer->string.data[writer->string.len + len] = '\0'; |
115 | 0 | writer->string.len += len; |
116 | |
|
117 | 0 | return len; |
118 | 0 | } |
119 | | |
120 | | /*********************************************************************/ |
121 | | |
122 | | static size_t FileWriterWriteF(Writer *writer, const char *fmt, va_list ap) |
123 | 0 | { |
124 | 0 | assert(writer != NULL); |
125 | 0 | return vfprintf(writer->file, fmt, ap); |
126 | 0 | } |
127 | | |
128 | | /*********************************************************************/ |
129 | | |
130 | | static size_t FileWriterWriteLen(Writer *writer, const char *str, size_t len_) |
131 | 0 | { |
132 | 0 | assert(writer != NULL); |
133 | 0 | size_t len = strnlen(str, len_); |
134 | |
|
135 | | #ifdef CFENGINE_TEST |
136 | | return CFENGINE_TEST_fwrite(str, 1, len, writer->file); |
137 | | #else |
138 | 0 | return fwrite(str, 1, len, writer->file); |
139 | 0 | #endif |
140 | 0 | } |
141 | | |
142 | | /*********************************************************************/ |
143 | | |
144 | | size_t WriterWriteF(Writer *writer, const char *fmt, ...) |
145 | 0 | { |
146 | 0 | va_list ap; |
147 | |
|
148 | 0 | va_start(ap, fmt); |
149 | 0 | size_t size = WriterWriteVF(writer, fmt, ap); |
150 | |
|
151 | 0 | va_end(ap); |
152 | 0 | return size; |
153 | 0 | } |
154 | | |
155 | | /*********************************************************************/ |
156 | | |
157 | | size_t WriterWriteVF(Writer *writer, const char *fmt, va_list ap) |
158 | 0 | { |
159 | 0 | assert(writer != NULL); |
160 | 0 | if (writer->type == WT_STRING) |
161 | 0 | { |
162 | 0 | char *str = NULL; |
163 | |
|
164 | 0 | xvasprintf(&str, fmt, ap); |
165 | 0 | size_t size = StringWriterWriteLen(writer, str, INT_MAX); |
166 | |
|
167 | 0 | free(str); |
168 | 0 | return size; |
169 | 0 | } |
170 | 0 | else |
171 | 0 | { |
172 | 0 | return FileWriterWriteF(writer, fmt, ap); |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | | /*********************************************************************/ |
177 | | |
178 | | size_t WriterWriteLen(Writer *writer, const char *str, size_t len) |
179 | 0 | { |
180 | 0 | assert(writer != NULL); |
181 | 0 | if (writer->type == WT_STRING) |
182 | 0 | { |
183 | 0 | return StringWriterWriteLen(writer, str, len); |
184 | 0 | } |
185 | 0 | else |
186 | 0 | { |
187 | 0 | return FileWriterWriteLen(writer, str, len); |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | /*********************************************************************/ |
192 | | |
193 | | size_t WriterWrite(Writer *writer, const char *str) |
194 | 0 | { |
195 | 0 | return WriterWriteLen(writer, str, INT_MAX); |
196 | 0 | } |
197 | | |
198 | | /*********************************************************************/ |
199 | | |
200 | | size_t WriterWriteChar(Writer *writer, char c) |
201 | 0 | { |
202 | 0 | assert(writer != NULL); |
203 | 0 | if (writer->type == WT_STRING) |
204 | 0 | { |
205 | 0 | return StringWriterWriteChar(writer, c); |
206 | 0 | } |
207 | 0 | else |
208 | 0 | { |
209 | 0 | char s[2] = { c, '\0' }; |
210 | 0 | return FileWriterWriteLen(writer, s, 1); |
211 | 0 | } |
212 | 0 | } |
213 | | |
214 | | /*********************************************************************/ |
215 | | |
216 | | size_t StringWriterLength(const Writer *writer) |
217 | 0 | { |
218 | 0 | assert(writer != NULL); |
219 | 0 | if (writer->type != WT_STRING) |
220 | 0 | { |
221 | 0 | ProgrammingError("Wrong writer type"); |
222 | 0 | } |
223 | | |
224 | 0 | return writer->string.len; |
225 | 0 | } |
226 | | |
227 | | /*********************************************************************/ |
228 | | |
229 | | const char *StringWriterData(const Writer *writer) |
230 | 0 | { |
231 | 0 | assert(writer != NULL); |
232 | 0 | if (writer->type != WT_STRING) |
233 | 0 | { |
234 | 0 | ProgrammingError("Wrong writer type"); |
235 | 0 | } |
236 | | |
237 | 0 | return writer->string.data; |
238 | 0 | } |
239 | | |
240 | | /*********************************************************************/ |
241 | | |
242 | | void WriterClose(Writer *writer) |
243 | 0 | { |
244 | 0 | assert(writer != NULL); |
245 | 0 | if (writer->type == WT_STRING) |
246 | 0 | { |
247 | 0 | free(writer->string.data); |
248 | 0 | } |
249 | 0 | else |
250 | 0 | { |
251 | | #ifdef CFENGINE_TEST |
252 | | CFENGINE_TEST_fclose(writer->file); |
253 | | #else |
254 | 0 | fclose(writer->file); |
255 | 0 | #endif |
256 | 0 | } |
257 | 0 | free(writer); |
258 | 0 | } |
259 | | |
260 | | /*********************************************************************/ |
261 | | |
262 | | char *StringWriterClose(Writer *writer) |
263 | | // NOTE: transfer of ownership for allocated return value |
264 | 0 | { |
265 | 0 | assert(writer != NULL); |
266 | |
|
267 | 0 | if (writer->type != WT_STRING) |
268 | 0 | { |
269 | 0 | ProgrammingError("Wrong writer type"); |
270 | 0 | } |
271 | 0 | char *data = writer->string.data; |
272 | |
|
273 | 0 | free(writer); |
274 | 0 | return data; |
275 | 0 | } |
276 | | |
277 | | FILE *FileWriterDetach(Writer *writer) |
278 | 0 | { |
279 | 0 | assert(writer != NULL); |
280 | 0 | if (writer->type != WT_FILE) |
281 | 0 | { |
282 | 0 | ProgrammingError("Wrong writer type"); |
283 | 0 | } |
284 | 0 | FILE *file = writer->file; |
285 | 0 | free(writer); |
286 | 0 | return file; |
287 | 0 | } |
288 | | |
289 | | static void WriterWriteOptions(Writer *w, const struct option options[], |
290 | | const char *const hints[]) |
291 | 0 | { |
292 | 0 | WriterWriteF(w, "\nOptions:\n"); |
293 | 0 | for (int i = 0; options[i].name != NULL; i++) |
294 | 0 | { |
295 | 0 | char short_option[] = ", -*"; |
296 | 0 | if (options[i].val < 128) |
297 | 0 | { |
298 | | // Within ASCII range, means there is a short option. |
299 | 0 | short_option[3] = options[i].val; |
300 | 0 | } |
301 | 0 | else |
302 | 0 | { |
303 | | // No short option. |
304 | 0 | short_option[0] = '\0'; |
305 | 0 | } |
306 | 0 | if (options[i].has_arg) |
307 | 0 | { |
308 | 0 | WriterWriteF(w, " --%-12s%s value - %s\n", options[i].name, |
309 | 0 | short_option, hints[i]); |
310 | 0 | } |
311 | 0 | else |
312 | 0 | { |
313 | 0 | WriterWriteF(w, " --%-12s%-10s - %s\n", options[i].name, |
314 | 0 | short_option, hints[i]); |
315 | 0 | } |
316 | 0 | } |
317 | 0 | } |
318 | | |
319 | | static void WriterWriteCommands(Writer *w, const Description *commands) |
320 | 0 | { |
321 | 0 | assert(commands != NULL); |
322 | 0 | WriterWriteF(w, "\nCommands:\n"); |
323 | 0 | for (int i = 0; commands[i].name != NULL; i++) |
324 | 0 | { |
325 | 0 | WriterWriteF(w, " %-12s - %s.\n", commands[i].name, |
326 | 0 | commands[i].description); |
327 | 0 | WriterWriteF(w, " %-12s Usage: %s\n", "", commands[i].usage); |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | | void WriterWriteHelp(Writer *w, const char *component, |
332 | | const struct option options[], const char *const hints[], |
333 | | const Description *commands, bool command_first, |
334 | | bool accepts_file_argument) |
335 | 0 | { |
336 | 0 | WriterWriteF(w, "Usage: %s%s [OPTIONS]%s%s\n", component, |
337 | 0 | (commands && command_first) ? " COMMAND" : "", |
338 | 0 | (commands && !command_first) ? " COMMAND" : "", |
339 | 0 | accepts_file_argument ? " [FILE]" : ""); |
340 | 0 | if ((commands != NULL) && command_first) |
341 | 0 | { |
342 | 0 | WriterWriteCommands(w, commands); |
343 | 0 | } |
344 | 0 | WriterWriteOptions(w, options, hints); |
345 | 0 | if ((commands != NULL) && !command_first) |
346 | 0 | { |
347 | 0 | WriterWriteCommands(w, commands); |
348 | 0 | } |
349 | 0 | WriterWriteF(w, "\nWebsite: https://cfengine.com\n"); |
350 | 0 | WriterWriteF(w, "This software is Copyright 2023 Northern.tech AS.\n "); |
351 | 0 | } |