Coverage Report

Created: 2023-12-12 06:10

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