cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

buffer.c
Go to the documentation of this file.
1 /*
2  Copyright 2020 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 <alloc.h>
27 #include <buffer.h>
28 #include <refcount.h>
29 #include <misc_lib.h>
30 #ifdef WITH_PCRE
31 #include <pcre_wrap.h>
32 #endif
33 #include <string_lib.h>
34 
35 Buffer *BufferNewWithCapacity(unsigned int initial_capacity)
36 {
37  Buffer *buffer = xmalloc(sizeof(Buffer));
38 
39  buffer->capacity = initial_capacity;
40  buffer->buffer = xmalloc(buffer->capacity);
41  buffer->buffer[0] = '\0';
42  buffer->mode = BUFFER_BEHAVIOR_CSTRING;
43  buffer->used = 0;
44 
45  return buffer;
46 }
47 
49 {
51 }
52 
53 static void ExpandIfNeeded(Buffer *buffer, unsigned int needed)
54 {
55  assert(buffer != NULL);
56  if (needed >= buffer->capacity)
57  {
58  size_t new_capacity = UpperPowerOfTwo(needed + 1);
59  buffer->buffer = xrealloc(buffer->buffer, new_capacity);
60  buffer->capacity = new_capacity;
61  }
62 }
63 
64 Buffer* BufferNewFrom(const char *data, unsigned int length)
65 {
66  Buffer *buffer = BufferNewWithCapacity(length + 1);
67  BufferAppend(buffer, data, length);
68 
69  return buffer;
70 }
71 
72 void BufferDestroy(Buffer *buffer)
73 {
74  if (buffer != NULL)
75  {
76  free(buffer->buffer);
77  free(buffer);
78  }
79 }
80 
81 char *BufferClose(Buffer *buffer)
82 {
83  assert(buffer != NULL);
84  char *detached = buffer->buffer;
85  free(buffer);
86 
87  return detached;
88 }
89 
90 Buffer *BufferCopy(const Buffer *source)
91 {
92  assert(source != NULL);
93  return BufferNewFrom(source->buffer, source->used);
94 }
95 
96 int BufferCompare(const Buffer *buffer1, const Buffer *buffer2)
97 {
98  assert(buffer1 != NULL);
99  assert(buffer2 != NULL);
100 
101  /*
102  * Rules for comparison:
103  * 2. Check the content
104  * 2.1. If modes are different, check until the first '\0'
105  * 2.2. If sizes are different, check until the first buffer ends.
106  */
107  if (buffer1->mode == buffer2->mode)
108  {
109  if (buffer1->mode == BUFFER_BEHAVIOR_CSTRING)
110  {
111  /*
112  * C String comparison
113  */
114  return strcmp(buffer1->buffer, buffer2->buffer);
115  }
116  else
117  {
118  /*
119  * BUFFER_BEHAVIOR_BYTEARRAY
120  * Byte by byte comparison
121  */
122  unsigned int i = 0;
123  if (buffer1->used < buffer2->used)
124  {
125  for (i = 0; i < buffer1->used; ++i)
126  {
127  if (buffer1->buffer[i] < buffer2->buffer[i])
128  {
129  return -1;
130  }
131  else if (buffer1->buffer[i] > buffer2->buffer[i])
132  {
133  return 1;
134  }
135  }
136  return -1;
137  }
138  else if (buffer1->used == buffer2->used)
139  {
140  for (i = 0; i < buffer1->used; ++i)
141  {
142  if (buffer1->buffer[i] < buffer2->buffer[i])
143  {
144  return -1;
145  }
146  else if (buffer1->buffer[i] > buffer2->buffer[i])
147  {
148  return 1;
149  }
150  }
151  }
152  else
153  {
154  for (i = 0; i < buffer2->used; ++i)
155  {
156  if (buffer1->buffer[i] < buffer2->buffer[i])
157  {
158  return -1;
159  }
160  else if (buffer1->buffer[i] > buffer2->buffer[i])
161  {
162  return 1;
163  }
164  }
165  return 1;
166  }
167  }
168  }
169  else
170  {
171  /*
172  * Mixed comparison
173  * Notice that every BYTEARRAY was born as a CSTRING.
174  * When we switch back to CSTRING we adjust the length to
175  * match the first '\0'.
176  */
177  unsigned int i = 0;
178  if (buffer1->used < buffer2->used)
179  {
180  for (i = 0; i < buffer1->used; ++i)
181  {
182  if (buffer1->buffer[i] < buffer2->buffer[i])
183  {
184  return -1;
185  }
186  else if (buffer1->buffer[i] > buffer2->buffer[i])
187  {
188  return 1;
189  }
190  }
191  return -1;
192  }
193  else if (buffer1->used == buffer2->used)
194  {
195  for (i = 0; i < buffer1->used; ++i)
196  {
197  if (buffer1->buffer[i] < buffer2->buffer[i])
198  {
199  return -1;
200  }
201  else if (buffer1->buffer[i] > buffer2->buffer[i])
202  {
203  return 1;
204  }
205  }
206  }
207  else
208  {
209  for (i = 0; i < buffer2->used; ++i)
210  {
211  if (buffer1->buffer[i] < buffer2->buffer[i])
212  {
213  return -1;
214  }
215  else if (buffer1->buffer[i] > buffer2->buffer[i])
216  {
217  return 1;
218  }
219  }
220  return 1;
221  }
222  }
223  /*
224  * We did all we could and the buffers seems to be equal.
225  */
226  return 0;
227 }
228 
229 void BufferSet(Buffer *buffer, const char *bytes, unsigned int length)
230 {
231  assert(buffer != NULL);
232  assert(bytes != NULL);
233 
234  BufferClear(buffer);
235 
236  BufferAppend(buffer, bytes, length);
237 }
238 
239 char *BufferGet(Buffer *buffer)
240 {
241  assert(buffer != NULL);
242  buffer->unsafe = true;
243  return buffer->buffer;
244 }
245 
246 void BufferAppendString(Buffer *buffer, const char *str)
247 {
248  assert(buffer != NULL);
249 
250  size_t len = strlen(str);
251  ExpandIfNeeded(buffer, buffer->used + len + 1);
252  memcpy(buffer->buffer + buffer->used, str, len);
253  buffer->used += len;
254  buffer->buffer[buffer->used] = '\0';
255 }
256 
257 void BufferTrimToMaxLength(Buffer *buffer, unsigned int max)
258 {
259  assert(buffer != NULL);
260 
261  if (buffer->used > max)
262  {
263  buffer->used = max;
264  // no need to call ExpandIfNeeded
265  buffer->buffer[buffer->used] = '\0';
266  }
267 }
268 
269 void BufferAppend(Buffer *buffer, const char *bytes, unsigned int length)
270 {
271  assert(buffer != NULL);
272  assert(bytes != NULL);
273 
274  if (length == 0)
275  {
276  return;
277  }
278 
279  switch (buffer->mode)
280  {
282  {
283  size_t actual_length = strnlen(bytes, length);
284  ExpandIfNeeded(buffer, buffer->used + actual_length + 1);
285  memcpy(buffer->buffer + buffer->used, bytes, actual_length);
286  buffer->used += actual_length;
287  buffer->buffer[buffer->used] = '\0';
288  }
289  break;
290 
292  ExpandIfNeeded(buffer, buffer->used + length);
293  memcpy(buffer->buffer + buffer->used, bytes, length);
294  buffer->used += length;
295  break;
296  }
297 }
298 
299 void BufferAppendChar(Buffer *buffer, char byte)
300 {
301  assert(buffer != NULL);
302  if (buffer->used < (buffer->capacity - 1))
303  {
304  buffer->buffer[buffer->used] = byte;
305  buffer->used++;
306 
307  if (buffer->mode == BUFFER_BEHAVIOR_CSTRING)
308  {
309  buffer->buffer[buffer->used] = '\0';
310  }
311  }
312  else
313  {
314  BufferAppend(buffer, &byte, 1);
315  }
316 }
317 
318 void BufferAppendF(Buffer *buffer, const char *format, ...)
319 {
320  assert(buffer != NULL);
321  assert(format != NULL);
322 
323  va_list ap;
324  va_list aq;
325  va_start(ap, format);
326  va_copy(aq, ap);
327 
328  int printed = vsnprintf(buffer->buffer + buffer->used, buffer->capacity - buffer->used, format, aq);
329  if (printed >= (buffer->capacity - buffer->used))
330  {
331  /*
332  * Allocate a larger buffer and retry.
333  * Now is when having a copy of the list pays off :-)
334  */
335  ExpandIfNeeded(buffer, buffer->used + printed);
336 
337  printed = vsnprintf(buffer->buffer + buffer->used, buffer->capacity - buffer->used, format, ap);
338  buffer->used += printed;
339  }
340  else
341  {
342  buffer->used += printed;
343  }
344  va_end(aq);
345  va_end(ap);
346 }
347 
348 int BufferPrintf(Buffer *buffer, const char *format, ...)
349 {
350  assert(buffer != NULL);
351  assert(format != NULL);
352  /*
353  * We declare two lists, in case we need to reiterate over the list because the buffer was
354  * too small.
355  */
356  va_list ap;
357  va_list aq;
358  va_start(ap, format);
359  va_copy(aq, ap);
360 
361  /*
362  * We don't know how big of a buffer we will need. It might be that we have enough space
363  * or it might be that we don't have enough space. Unfortunately, we cannot reiterate over
364  * a va_list, so our only solution is to tell the caller to retry the call. We signal this
365  * by returning zero. Before doing that we increase the buffer to a suitable size.
366  * The tricky part is the implicit sharing and the reference counting, if we are not shared then
367  * everything is easy, however if we are shared then we need a different strategy.
368  */
369  int printed = vsnprintf(buffer->buffer, buffer->capacity, format, aq);
370  if (printed >= buffer->capacity)
371  {
372  /*
373  * Allocate a larger buffer and retry.
374  * Now is when having a copy of the list pays off :-)
375  */
376  ExpandIfNeeded(buffer, printed);
377 
378  buffer->used = 0;
379  printed = vsnprintf(buffer->buffer, buffer->capacity, format, ap);
380  buffer->used = printed;
381  }
382  else
383  {
384  buffer->used = printed;
385  }
386  va_end(aq);
387  va_end(ap);
388  return printed;
389 }
390 
391 // NB! Make sure to sanitize format if taken from user input
392 int BufferVPrintf(Buffer *buffer, const char *format, va_list ap)
393 {
394  assert(buffer != NULL);
395  assert(format != NULL);
396  va_list aq;
397  va_copy(aq, ap);
398 
399  /*
400  * We don't know how big of a buffer we will need. It might be that we have enough space
401  * or it might be that we don't have enough space. Unfortunately, we cannot reiterate over
402  * a va_list, so our only solution is to tell the caller to retry the call. We signal this
403  * by returning zero. Before doing that we increase the buffer to a suitable size.
404  * The tricky part is the implicit sharing and the reference counting, if we are not shared then
405  * everything is easy, however if we are shared then we need a different strategy.
406  */
407 
408  int printed = vsnprintf(buffer->buffer, buffer->capacity, format, aq);
409  if (printed >= buffer->capacity)
410  {
411  ExpandIfNeeded(buffer, printed);
412  buffer->used = 0;
413  printed = vsnprintf(buffer->buffer, buffer->capacity, format, ap);
414  buffer->used = printed;
415  }
416  else
417  {
418  buffer->used = printed;
419  }
420  va_end(aq);
421  return printed;
422 }
423 
424 #ifdef WITH_PCRE
425 // returns NULL on success, otherwise an error string
426 const char* BufferSearchAndReplace(Buffer *buffer, const char *pattern, const char *substitute, const char *options)
427 {
428  assert(buffer != NULL);
429  assert(pattern);
430  assert(substitute);
431  assert(options);
432 
433  int err;
434 
435  pcre_wrap_job *job = pcre_wrap_compile(pattern, substitute, options, &err);
436  if (job == NULL)
437  {
438  return pcre_wrap_strerror(err);
439  }
440 
441  size_t length = BufferSize(buffer);
442  char *result;
443  if (0 > (err = pcre_wrap_execute(job, (char*)BufferData(buffer), length, &result, &length)))
444  {
445  return pcre_wrap_strerror(err);
446  }
447 
448  BufferSet(buffer, result, length);
449  free(result);
450  pcre_wrap_free_job(job);
451 
452  return NULL;
453 }
454 
455 #endif // WITH_PCRE
456 
457 void BufferClear(Buffer *buffer)
458 {
459  assert(buffer != NULL);
460  buffer->used = 0;
461  buffer->buffer[0] = '\0';
462 }
463 
464 unsigned int BufferSize(const Buffer *buffer)
465 {
466  assert(buffer != NULL);
467  return buffer != NULL ? buffer->used : 0;
468 }
469 
470 const char *BufferData(const Buffer *buffer)
471 {
472  assert(buffer != NULL);
473  return buffer != NULL ? buffer->buffer : NULL;
474 }
475 
476 void BufferCanonify(Buffer *buffer)
477 {
478  assert(buffer != NULL);
479  if (buffer != NULL &&
480  buffer->buffer != NULL)
481  {
482  CanonifyNameInPlace(buffer->buffer);
483  }
484 }
485 
487 {
488  assert(buffer != NULL);
489  return buffer != NULL ? buffer->mode : BUFFER_BEHAVIOR_BYTEARRAY;
490 }
491 
493 {
494  assert(buffer != NULL);
495  assert(mode == BUFFER_BEHAVIOR_CSTRING || mode == BUFFER_BEHAVIOR_BYTEARRAY);
496  /*
497  * If we switch from BYTEARRAY mode to CSTRING then we need to adjust the
498  * length to the first '\0'. This makes our life easier in the long run.
499  */
500  if (BUFFER_BEHAVIOR_CSTRING == mode)
501  {
502  for (unsigned int i = 0; i < buffer->used; ++i)
503  {
504  if (buffer->buffer[i] == '\0')
505  {
506  buffer->used = i;
507  break;
508  }
509  }
510  }
511  buffer->mode = mode;
512 }
513 
514 Buffer* BufferFilter(Buffer *buffer, BufferFilterFn filter, const bool invert)
515 {
516  assert(buffer != NULL);
517 
518  Buffer *filtered = BufferNew();
519  for (unsigned int i = 0; i < buffer->used; ++i)
520  {
521  bool test = (*filter)(buffer->buffer[i]);
522  if (invert)
523  {
524  test = !test;
525  }
526 
527  if (test)
528  {
529  BufferAppendChar(filtered, buffer->buffer[i]);
530  }
531  }
532 
533  return filtered;
534 }
535 
536 void BufferRewrite(Buffer *buffer, BufferFilterFn filter, const bool invert)
537 {
538  assert(buffer != NULL);
539 
540  Buffer *rewrite = BufferFilter(buffer, filter, invert);
541  BufferSet(buffer, BufferData(rewrite), BufferSize(rewrite));
542  BufferDestroy(rewrite);
543 }
544 
545 unsigned BufferCapacity(const Buffer *buffer)
546 {
547  assert(buffer != NULL);
548  return buffer != NULL ? buffer->capacity : 0;
549 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
void * xrealloc(void *ptr, size_t size)
Definition: alloc.c:51
void BufferDestroy(Buffer *buffer)
Destroys a buffer and frees the memory associated with it.
Definition: buffer.c:72
static void ExpandIfNeeded(Buffer *buffer, unsigned int needed)
Definition: buffer.c:53
Buffer * BufferNew(void)
Buffer initialization routine.
Definition: buffer.c:48
Buffer * BufferNewFrom(const char *data, unsigned int length)
Initializes a buffer based on a const char pointer.
Definition: buffer.c:64
char * BufferGet(Buffer *buffer)
This functions allows direct access to the storage inside Buffer.
Definition: buffer.c:239
int BufferCompare(const Buffer *buffer1, const Buffer *buffer2)
Compares two buffers. Uses the same semantic as strcmp.
Definition: buffer.c:96
const char * BufferData(const Buffer *buffer)
Provides a pointer to the internal data.
Definition: buffer.c:470
void BufferSetMode(Buffer *buffer, BufferBehavior mode)
Sets the operational mode of the buffer.
Definition: buffer.c:492
int BufferPrintf(Buffer *buffer, const char *format,...)
Stores complex data on the buffer.
Definition: buffer.c:348
unsigned int BufferSize(const Buffer *buffer)
Returns the size of the buffer.
Definition: buffer.c:464
unsigned BufferCapacity(const Buffer *buffer)
Definition: buffer.c:545
void BufferClear(Buffer *buffer)
Clears the buffer.
Definition: buffer.c:457
Buffer * BufferNewWithCapacity(unsigned int initial_capacity)
Allocates and setup a buffer with a capacity different than the default capacity.
Definition: buffer.c:35
void BufferAppendF(Buffer *buffer, const char *format,...)
Definition: buffer.c:318
void BufferAppendChar(Buffer *buffer, char byte)
Appends a char to an existing buffer.
Definition: buffer.c:299
void BufferSet(Buffer *buffer, const char *bytes, unsigned int length)
Replaces the current content of the buffer with the given string.
Definition: buffer.c:229
void BufferAppend(Buffer *buffer, const char *bytes, unsigned int length)
Definition: buffer.c:269
Buffer * BufferFilter(Buffer *buffer, BufferFilterFn filter, const bool invert)
Returns a filtered copy of a Buffer.
Definition: buffer.c:514
BufferBehavior BufferMode(const Buffer *buffer)
Returns the current mode of operation of the buffer.
Definition: buffer.c:486
char * BufferClose(Buffer *buffer)
Destroys a buffer structure returning the its contents.
Definition: buffer.c:81
void BufferRewrite(Buffer *buffer, BufferFilterFn filter, const bool invert)
Filters a Buffer in place.
Definition: buffer.c:536
void BufferCanonify(Buffer *buffer)
Canonify a buffer in place: replace [^0-9a-zA-Z] with '_'.
Definition: buffer.c:476
int BufferVPrintf(Buffer *buffer, const char *format, va_list ap)
Stores complex data on the buffer.
Definition: buffer.c:392
Buffer * BufferCopy(const Buffer *source)
Creates a shallow copy of the source buffer.
Definition: buffer.c:90
void BufferAppendString(Buffer *buffer, const char *str)
Definition: buffer.c:246
void BufferTrimToMaxLength(Buffer *buffer, unsigned int max)
Trim a buffer to be at most max bytes.
Definition: buffer.c:257
BufferBehavior
Buffer implementation.
Definition: buffer.h:42
@ BUFFER_BEHAVIOR_BYTEARRAY
Definition: buffer.h:44
@ BUFFER_BEHAVIOR_CSTRING
Definition: buffer.h:43
#define DEFAULT_BUFFER_CAPACITY
Definition: buffer.h:47
bool(* BufferFilterFn)(char item)
Definition: buffer.h:59
const char * BufferSearchAndReplace(Buffer *buffer, const char *pattern, const char *substitute, const char *options)
Does a PCRE search and replace on the buffer data.
void free(void *)
#define NULL
Definition: getopt1.c:56
size_t UpperPowerOfTwo(size_t v)
Definition: misc_lib.c:41
int pcre_wrap_execute(pcre_wrap_job *job, char *subject, size_t subject_length, char **result, size_t *result_length)
Definition: pcre_wrap.c:343
pcre_wrap_job * pcre_wrap_free_job(pcre_wrap_job *job)
Definition: pcre_wrap.c:251
pcre_wrap_job * pcre_wrap_compile(const char *pattern, const char *substitute, const char *options, int *errptr)
Definition: pcre_wrap.c:283
const char * pcre_wrap_strerror(const int error)
Definition: pcre_wrap.c:21
void CanonifyNameInPlace(char *s)
Definition: string_lib.c:1574
size_t strnlen(const char *str, size_t maxlen)
Definition: strnlen.c:32
Definition: buffer.h:50
unsigned int capacity
Definition: buffer.h:53
char * buffer
Definition: buffer.h:51
bool unsafe
Definition: buffer.h:55
unsigned int used
Definition: buffer.h:54
BufferBehavior mode
Definition: buffer.h:52