Coverage Report

Created: 2023-06-07 07:02

/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
}