1 /*
2 Copyright 2021 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
FileWriter(FILE * file)56 Writer *FileWriter(FILE *file)
57 {
58 Writer *writer = xcalloc(1, sizeof(Writer));
59
60 writer->type = WT_FILE;
61 writer->file = file;
62 return writer;
63 }
64
65 /*********************************************************************/
66
StringWriter(void)67 Writer *StringWriter(void)
68 {
69 Writer *writer = xcalloc(1, sizeof(Writer));
70
71 writer->type = WT_STRING;
72 writer->string.data = xstrdup("");
73 writer->string.allocated = 1;
74 writer->string.len = 0;
75 return writer;
76 }
77
78 /*********************************************************************/
79
StringWriterReallocate(Writer * writer,size_t extra_length)80 static void StringWriterReallocate(Writer *writer, size_t extra_length)
81 {
82 assert(writer != NULL);
83 writer->string.allocated = MAX(writer->string.allocated * 2, writer->string.len + extra_length + 1);
84 writer->string.data = xrealloc(writer->string.data, writer->string.allocated);
85 }
86
StringWriterWriteChar(Writer * writer,char c)87 static size_t StringWriterWriteChar(Writer *writer, char c)
88 {
89 assert(writer != NULL);
90 if (writer->string.len + 2 > writer->string.allocated)
91 {
92 StringWriterReallocate(writer, 2);
93 }
94
95 writer->string.data[writer->string.len] = c;
96 writer->string.data[writer->string.len + 1] = '\0';
97 writer->string.len++;
98
99 return 1;
100 }
101
StringWriterWriteLen(Writer * writer,const char * str,size_t len_)102 static size_t StringWriterWriteLen(Writer *writer, const char *str, size_t len_)
103 {
104 assert(writer != NULL);
105 /* NB: str[:len_] may come from read(), which hasn't '\0'-terminated */
106 size_t len = strnlen(str, len_);
107
108 if (writer->string.len + len + 1 > writer->string.allocated)
109 {
110 StringWriterReallocate(writer, len);
111 }
112
113 memcpy(writer->string.data + writer->string.len, str, len);
114 writer->string.data[writer->string.len + len] = '\0';
115 writer->string.len += len;
116
117 return len;
118 }
119
120 /*********************************************************************/
121
FileWriterWriteF(Writer * writer,const char * fmt,va_list ap)122 static size_t FileWriterWriteF(Writer *writer, const char *fmt, va_list ap)
123 {
124 assert(writer != NULL);
125 return vfprintf(writer->file, fmt, ap);
126 }
127
128 /*********************************************************************/
129
FileWriterWriteLen(Writer * writer,const char * str,size_t len_)130 static size_t FileWriterWriteLen(Writer *writer, const char *str, size_t len_)
131 {
132 assert(writer != NULL);
133 size_t len = strnlen(str, len_);
134
135 #ifdef CFENGINE_TEST
136 return CFENGINE_TEST_fwrite(str, 1, len, writer->file);
137 #else
138 return fwrite(str, 1, len, writer->file);
139 #endif
140 }
141
142 /*********************************************************************/
143
WriterWriteF(Writer * writer,const char * fmt,...)144 size_t WriterWriteF(Writer *writer, const char *fmt, ...)
145 {
146 va_list ap;
147
148 va_start(ap, fmt);
149 size_t size = WriterWriteVF(writer, fmt, ap);
150
151 va_end(ap);
152 return size;
153 }
154
155 /*********************************************************************/
156
WriterWriteVF(Writer * writer,const char * fmt,va_list ap)157 size_t WriterWriteVF(Writer *writer, const char *fmt, va_list ap)
158 {
159 assert(writer != NULL);
160 if (writer->type == WT_STRING)
161 {
162 char *str = NULL;
163
164 xvasprintf(&str, fmt, ap);
165 size_t size = StringWriterWriteLen(writer, str, INT_MAX);
166
167 free(str);
168 return size;
169 }
170 else
171 {
172 return FileWriterWriteF(writer, fmt, ap);
173 }
174 }
175
176 /*********************************************************************/
177
WriterWriteLen(Writer * writer,const char * str,size_t len)178 size_t WriterWriteLen(Writer *writer, const char *str, size_t len)
179 {
180 assert(writer != NULL);
181 if (writer->type == WT_STRING)
182 {
183 return StringWriterWriteLen(writer, str, len);
184 }
185 else
186 {
187 return FileWriterWriteLen(writer, str, len);
188 }
189 }
190
191 /*********************************************************************/
192
WriterWrite(Writer * writer,const char * str)193 size_t WriterWrite(Writer *writer, const char *str)
194 {
195 return WriterWriteLen(writer, str, INT_MAX);
196 }
197
198 /*********************************************************************/
199
WriterWriteChar(Writer * writer,char c)200 size_t WriterWriteChar(Writer *writer, char c)
201 {
202 assert(writer != NULL);
203 if (writer->type == WT_STRING)
204 {
205 return StringWriterWriteChar(writer, c);
206 }
207 else
208 {
209 char s[2] = { c, '\0' };
210 return FileWriterWriteLen(writer, s, 1);
211 }
212 }
213
214 /*********************************************************************/
215
StringWriterLength(const Writer * writer)216 size_t StringWriterLength(const Writer *writer)
217 {
218 assert(writer != NULL);
219 if (writer->type != WT_STRING)
220 {
221 ProgrammingError("Wrong writer type");
222 }
223
224 return writer->string.len;
225 }
226
227 /*********************************************************************/
228
StringWriterData(const Writer * writer)229 const char *StringWriterData(const Writer *writer)
230 {
231 assert(writer != NULL);
232 if (writer->type != WT_STRING)
233 {
234 ProgrammingError("Wrong writer type");
235 }
236
237 return writer->string.data;
238 }
239
240 /*********************************************************************/
241
WriterClose(Writer * writer)242 void WriterClose(Writer *writer)
243 {
244 assert(writer != NULL);
245 if (writer->type == WT_STRING)
246 {
247 free(writer->string.data);
248 }
249 else
250 {
251 #ifdef CFENGINE_TEST
252 CFENGINE_TEST_fclose(writer->file);
253 #else
254 fclose(writer->file);
255 #endif
256 }
257 free(writer);
258 }
259
260 /*********************************************************************/
261
StringWriterClose(Writer * writer)262 char *StringWriterClose(Writer *writer)
263 // NOTE: transfer of ownership for allocated return value
264 {
265 assert(writer != NULL);
266
267 if (writer->type != WT_STRING)
268 {
269 ProgrammingError("Wrong writer type");
270 }
271 char *data = writer->string.data;
272
273 free(writer);
274 return data;
275 }
276
FileWriterDetach(Writer * writer)277 FILE *FileWriterDetach(Writer *writer)
278 {
279 assert(writer != NULL);
280 if (writer->type != WT_FILE)
281 {
282 ProgrammingError("Wrong writer type");
283 }
284 FILE *file = writer->file;
285 free(writer);
286 return file;
287 }
288
WriterWriteOptions(Writer * w,const struct option options[],const char * const hints[])289 static void WriterWriteOptions(Writer *w, const struct option options[],
290 const char *const hints[])
291 {
292 WriterWriteF(w, "\nOptions:\n");
293 for (int i = 0; options[i].name != NULL; i++)
294 {
295 char short_option[] = ", -*";
296 if (options[i].val < 128)
297 {
298 // Within ASCII range, means there is a short option.
299 short_option[3] = options[i].val;
300 }
301 else
302 {
303 // No short option.
304 short_option[0] = '\0';
305 }
306 if (options[i].has_arg)
307 {
308 WriterWriteF(w, " --%-12s%s value - %s\n", options[i].name,
309 short_option, hints[i]);
310 }
311 else
312 {
313 WriterWriteF(w, " --%-12s%-10s - %s\n", options[i].name,
314 short_option, hints[i]);
315 }
316 }
317 }
318
WriterWriteCommands(Writer * w,const Description * commands)319 static void WriterWriteCommands(Writer *w, const Description *commands)
320 {
321 assert(commands != NULL);
322 WriterWriteF(w, "\nCommands:\n");
323 for (int i = 0; commands[i].name != NULL; i++)
324 {
325 WriterWriteF(w, " %-12s - %s.\n", commands[i].name,
326 commands[i].description);
327 WriterWriteF(w, " %-12s Usage: %s\n", "", commands[i].usage);
328 }
329 }
330
WriterWriteHelp(Writer * w,const char * component,const struct option options[],const char * const hints[],const Description * commands,bool command_first,bool accepts_file_argument)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 {
336 WriterWriteF(w, "Usage: %s%s [OPTIONS]%s%s\n", component,
337 (commands && command_first) ? " COMMAND" : "",
338 (commands && !command_first) ? " COMMAND" : "",
339 accepts_file_argument ? " [FILE]" : "");
340 if ((commands != NULL) && command_first)
341 {
342 WriterWriteCommands(w, commands);
343 }
344 WriterWriteOptions(w, options, hints);
345 if ((commands != NULL) && !command_first)
346 {
347 WriterWriteCommands(w, commands);
348 }
349 WriterWriteF(w, "\nWebsite: http://www.cfengine.com\n");
350 WriterWriteF(w, "This software is Copyright (C) "
351 "2008,2010-present Northern.tech AS.\n");
352 }
353