]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
29100ef2 | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2016 Nicira, Inc. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
3e8a2ad1 | 18 | #include "openvswitch/dynamic-string.h" |
34582733 | 19 | #include <inttypes.h> |
064af421 BP |
20 | #include <stdlib.h> |
21 | #include <string.h> | |
22 | #include <time.h> | |
23 | #include "timeval.h" | |
24 | #include "util.h" | |
25 | ||
541bc79f | 26 | /* Initializes 'ds' as an empty string buffer. */ |
064af421 BP |
27 | void |
28 | ds_init(struct ds *ds) | |
29 | { | |
30 | ds->string = NULL; | |
31 | ds->length = 0; | |
32 | ds->allocated = 0; | |
33 | } | |
34 | ||
541bc79f BP |
35 | /* Sets 'ds''s length to 0, effectively clearing any existing content. Does |
36 | * not free any memory. */ | |
064af421 | 37 | void |
d295e8e9 | 38 | ds_clear(struct ds *ds) |
064af421 BP |
39 | { |
40 | ds->length = 0; | |
41 | } | |
42 | ||
541bc79f BP |
43 | /* Reduces 'ds''s length to no more than 'new_length'. (If its length is |
44 | * already 'new_length' or less, does nothing.) */ | |
064af421 BP |
45 | void |
46 | ds_truncate(struct ds *ds, size_t new_length) | |
47 | { | |
48 | if (ds->length > new_length) { | |
49 | ds->length = new_length; | |
50 | ds->string[new_length] = '\0'; | |
51 | } | |
52 | } | |
53 | ||
541bc79f BP |
54 | /* Ensures that at least 'min_length + 1' bytes (including space for a null |
55 | * terminator) are allocated for ds->string, allocating or reallocating memory | |
56 | * as necessary. */ | |
064af421 BP |
57 | void |
58 | ds_reserve(struct ds *ds, size_t min_length) | |
59 | { | |
60 | if (min_length > ds->allocated || !ds->string) { | |
61 | ds->allocated += MAX(min_length, ds->allocated); | |
62 | ds->allocated = MAX(8, ds->allocated); | |
63 | ds->string = xrealloc(ds->string, ds->allocated + 1); | |
64 | } | |
65 | } | |
66 | ||
541bc79f BP |
67 | /* Appends space for 'n' bytes to the end of 'ds->string', increasing |
68 | * 'ds->length' by the same amount, and returns the first appended byte. The | |
69 | * caller should fill in all 'n' bytes starting at the return value. */ | |
064af421 BP |
70 | char * |
71 | ds_put_uninit(struct ds *ds, size_t n) | |
72 | { | |
73 | ds_reserve(ds, ds->length + n); | |
74 | ds->length += n; | |
75 | ds->string[ds->length] = '\0'; | |
76 | return &ds->string[ds->length - n]; | |
77 | } | |
78 | ||
79 | void | |
36c501fe | 80 | ds_put_char__(struct ds *ds, char c) |
064af421 BP |
81 | { |
82 | *ds_put_uninit(ds, 1) = c; | |
83 | } | |
84 | ||
f38b84ea BP |
85 | /* Appends unicode code point 'uc' to 'ds' in UTF-8 encoding. */ |
86 | void | |
87 | ds_put_utf8(struct ds *ds, int uc) | |
88 | { | |
89 | if (uc <= 0x7f) { | |
90 | ds_put_char(ds, uc); | |
91 | } else if (uc <= 0x7ff) { | |
92 | ds_put_char(ds, 0xc0 | (uc >> 6)); | |
93 | ds_put_char(ds, 0x80 | (uc & 0x3f)); | |
94 | } else if (uc <= 0xffff) { | |
95 | ds_put_char(ds, 0xe0 | (uc >> 12)); | |
96 | ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f)); | |
97 | ds_put_char(ds, 0x80 | (uc & 0x3f)); | |
98 | } else if (uc <= 0x10ffff) { | |
99 | ds_put_char(ds, 0xf0 | (uc >> 18)); | |
100 | ds_put_char(ds, 0x80 | ((uc >> 12) & 0x3f)); | |
101 | ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f)); | |
102 | ds_put_char(ds, 0x80 | (uc & 0x3f)); | |
103 | } else { | |
104 | /* Invalid code point. Insert the Unicode general substitute | |
105 | * REPLACEMENT CHARACTER. */ | |
106 | ds_put_utf8(ds, 0xfffd); | |
107 | } | |
108 | } | |
109 | ||
064af421 BP |
110 | void |
111 | ds_put_char_multiple(struct ds *ds, char c, size_t n) | |
112 | { | |
113 | memset(ds_put_uninit(ds, n), c, n); | |
114 | } | |
115 | ||
116 | void | |
117 | ds_put_buffer(struct ds *ds, const char *s, size_t n) | |
118 | { | |
119 | memcpy(ds_put_uninit(ds, n), s, n); | |
120 | } | |
121 | ||
122 | void | |
123 | ds_put_cstr(struct ds *ds, const char *s) | |
124 | { | |
125 | size_t s_len = strlen(s); | |
126 | memcpy(ds_put_uninit(ds, s_len), s, s_len); | |
127 | } | |
128 | ||
1fd13cde BP |
129 | void |
130 | ds_put_and_free_cstr(struct ds *ds, char *s) | |
131 | { | |
132 | ds_put_cstr(ds, s); | |
133 | free(s); | |
134 | } | |
135 | ||
064af421 BP |
136 | void |
137 | ds_put_format(struct ds *ds, const char *format, ...) | |
138 | { | |
139 | va_list args; | |
140 | ||
141 | va_start(args, format); | |
142 | ds_put_format_valist(ds, format, args); | |
143 | va_end(args); | |
144 | } | |
145 | ||
146 | void | |
147 | ds_put_format_valist(struct ds *ds, const char *format, va_list args_) | |
148 | { | |
149 | va_list args; | |
150 | size_t available; | |
151 | int needed; | |
152 | ||
153 | va_copy(args, args_); | |
154 | available = ds->string ? ds->allocated - ds->length + 1 : 0; | |
155 | needed = vsnprintf(&ds->string[ds->length], available, format, args); | |
156 | va_end(args); | |
157 | ||
158 | if (needed < available) { | |
159 | ds->length += needed; | |
160 | } else { | |
064af421 BP |
161 | ds_reserve(ds, ds->length + needed); |
162 | ||
163 | va_copy(args, args_); | |
164 | available = ds->allocated - ds->length + 1; | |
165 | needed = vsnprintf(&ds->string[ds->length], available, format, args); | |
166 | va_end(args); | |
167 | ||
cb22974d | 168 | ovs_assert(needed < available); |
064af421 BP |
169 | ds->length += needed; |
170 | } | |
171 | } | |
172 | ||
173 | void | |
d295e8e9 | 174 | ds_put_printable(struct ds *ds, const char *s, size_t n) |
064af421 BP |
175 | { |
176 | ds_reserve(ds, ds->length + n); | |
177 | while (n-- > 0) { | |
178 | unsigned char c = *s++; | |
179 | if (c < 0x20 || c > 0x7e || c == '\\' || c == '"') { | |
180 | ds_put_format(ds, "\\%03o", (int) c); | |
181 | } else { | |
182 | ds_put_char(ds, c); | |
183 | } | |
184 | } | |
185 | } | |
186 | ||
2b31d8e7 PI |
187 | /* Writes the current time with optional millisecond resolution to 'string' |
188 | * based on 'template'. | |
189 | * The current time is either localtime or UTC based on 'utc'. */ | |
064af421 | 190 | void |
2b31d8e7 PI |
191 | ds_put_strftime_msec(struct ds *ds, const char *template, long long int when, |
192 | bool utc) | |
064af421 | 193 | { |
2b31d8e7 | 194 | struct tm_msec tm; |
b5d29991 | 195 | if (utc) { |
2b31d8e7 | 196 | gmtime_msec(when, &tm); |
b5d29991 | 197 | } else { |
2b31d8e7 | 198 | localtime_msec(when, &tm); |
064af421 | 199 | } |
b5d29991 | 200 | |
064af421 BP |
201 | for (;;) { |
202 | size_t avail = ds->string ? ds->allocated - ds->length + 1 : 0; | |
2b31d8e7 PI |
203 | size_t used = strftime_msec(&ds->string[ds->length], avail, template, |
204 | &tm); | |
064af421 BP |
205 | if (used) { |
206 | ds->length += used; | |
207 | return; | |
208 | } | |
d295e8e9 | 209 | ds_reserve(ds, ds->length + (avail < 32 ? 64 : 2 * avail)); |
064af421 BP |
210 | } |
211 | } | |
212 | ||
3e78870d BP |
213 | /* Returns a malloc()'d string for time 'when' based on 'template', in local |
214 | * time or UTC based on 'utc'. */ | |
215 | char * | |
2b31d8e7 | 216 | xastrftime_msec(const char *template, long long int when, bool utc) |
3e78870d BP |
217 | { |
218 | struct ds s; | |
219 | ||
220 | ds_init(&s); | |
2b31d8e7 | 221 | ds_put_strftime_msec(&s, template, when, utc); |
3e78870d BP |
222 | return s.string; |
223 | } | |
224 | ||
064af421 BP |
225 | int |
226 | ds_get_line(struct ds *ds, FILE *file) | |
227 | { | |
228 | ds_clear(ds); | |
229 | for (;;) { | |
230 | int c = getc(file); | |
231 | if (c == EOF) { | |
232 | return ds->length ? 0 : EOF; | |
233 | } else if (c == '\n') { | |
234 | return 0; | |
235 | } else { | |
236 | ds_put_char(ds, c); | |
237 | } | |
238 | } | |
239 | } | |
240 | ||
dd8101bc BP |
241 | /* Reads a line from 'file' into 'ds', clearing anything initially in 'ds'. |
242 | * Deletes comments introduced by "#" and skips lines that contains only white | |
243 | * space (after deleting comments). | |
244 | * | |
bdda5aca BP |
245 | * If 'line_numberp' is nonnull, increments '*line_numberp' by the number of |
246 | * lines read from 'file'. | |
247 | * | |
dd8101bc BP |
248 | * Returns 0 if successful, EOF if no non-blank line was found. */ |
249 | int | |
bdda5aca | 250 | ds_get_preprocessed_line(struct ds *ds, FILE *file, int *line_numberp) |
dd8101bc BP |
251 | { |
252 | while (!ds_get_line(ds, file)) { | |
253 | char *line = ds_cstr(ds); | |
254 | char *comment; | |
255 | ||
bdda5aca BP |
256 | if (line_numberp) { |
257 | ++*line_numberp; | |
258 | } | |
259 | ||
dd8101bc BP |
260 | /* Delete comments. */ |
261 | comment = strchr(line, '#'); | |
262 | if (comment) { | |
263 | *comment = '\0'; | |
264 | } | |
265 | ||
266 | /* Return successfully unless the line is all spaces. */ | |
267 | if (line[strspn(line, " \t\n")] != '\0') { | |
268 | return 0; | |
269 | } | |
270 | } | |
271 | return EOF; | |
272 | } | |
273 | ||
06d7ae7d BP |
274 | /* Reads a line from 'file' into 'ds' and does some preprocessing on it: |
275 | * | |
276 | * - If the line begins with #, prints it on stdout and reads the next line. | |
277 | * | |
278 | * - Otherwise, if the line contains an # somewhere else, strips it and | |
279 | * everything following it (as a comment). | |
280 | * | |
281 | * - If (after comment removal) the line contains only white space, prints | |
282 | * a blank line on stdout and reads the next line. | |
283 | * | |
284 | * - Otherwise, returns the line to the caller. | |
285 | * | |
286 | * This is useful in some of the OVS tests, where we want to check that parsing | |
287 | * and then re-formatting some kind of data does not change it, but we also | |
288 | * want to be able to put comments in the input. | |
289 | * | |
290 | * Returns 0 if successful, EOF if no non-blank line was found. */ | |
291 | int | |
292 | ds_get_test_line(struct ds *ds, FILE *file) | |
293 | { | |
294 | for (;;) { | |
295 | char *s, *comment; | |
296 | int retval; | |
297 | ||
298 | retval = ds_get_line(ds, file); | |
299 | if (retval) { | |
300 | return retval; | |
301 | } | |
302 | ||
303 | s = ds_cstr(ds); | |
304 | if (*s == '#') { | |
305 | puts(s); | |
306 | continue; | |
307 | } | |
308 | ||
309 | comment = strchr(s, '#'); | |
310 | if (comment) { | |
311 | *comment = '\0'; | |
312 | } | |
313 | if (s[strspn(s, " \t\n")] == '\0') { | |
314 | putchar('\n'); | |
315 | continue; | |
316 | } | |
317 | ||
318 | return 0; | |
319 | } | |
320 | } | |
321 | ||
064af421 BP |
322 | char * |
323 | ds_cstr(struct ds *ds) | |
324 | { | |
325 | if (!ds->string) { | |
326 | ds_reserve(ds, 0); | |
327 | } | |
328 | ds->string[ds->length] = '\0'; | |
329 | return ds->string; | |
330 | } | |
331 | ||
5f98eed4 BP |
332 | const char * |
333 | ds_cstr_ro(const struct ds *ds) | |
334 | { | |
ebc56baa | 335 | return ds_cstr(CONST_CAST(struct ds *, ds)); |
5f98eed4 BP |
336 | } |
337 | ||
48718554 BP |
338 | /* Returns a null-terminated string representing the current contents of 'ds', |
339 | * which the caller is expected to free with free(), then clears the contents | |
340 | * of 'ds'. */ | |
341 | char * | |
342 | ds_steal_cstr(struct ds *ds) | |
343 | { | |
344 | char *s = ds_cstr(ds); | |
345 | ds_init(ds); | |
346 | return s; | |
347 | } | |
348 | ||
064af421 BP |
349 | void |
350 | ds_destroy(struct ds *ds) | |
351 | { | |
352 | free(ds->string); | |
353 | } | |
354 | ||
e83fd213 BP |
355 | /* Swaps the content of 'a' and 'b'. */ |
356 | void | |
357 | ds_swap(struct ds *a, struct ds *b) | |
358 | { | |
359 | struct ds temp = *a; | |
360 | *a = *b; | |
361 | *b = temp; | |
362 | } | |
363 | ||
e7ae59f9 JG |
364 | void |
365 | ds_put_hex(struct ds *ds, const void *buf_, size_t size) | |
366 | { | |
367 | const uint8_t *buf = buf_; | |
368 | bool printed = false; | |
369 | int i; | |
370 | ||
371 | for (i = 0; i < size; i++) { | |
372 | uint8_t val = buf[i]; | |
373 | if (val || printed) { | |
374 | if (!printed) { | |
375 | ds_put_format(ds, "0x%"PRIx8, val); | |
376 | } else { | |
377 | ds_put_format(ds, "%02"PRIx8, val); | |
378 | } | |
379 | printed = true; | |
380 | } | |
381 | } | |
382 | if (!printed) { | |
383 | ds_put_char(ds, '0'); | |
384 | } | |
385 | } | |
386 | ||
064af421 BP |
387 | /* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per |
388 | * line. Numeric offsets are also included, starting at 'ofs' for the first | |
389 | * byte in 'buf'. If 'ascii' is true then the corresponding ASCII characters | |
390 | * are also rendered alongside. */ | |
391 | void | |
392 | ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, | |
393 | uintptr_t ofs, bool ascii) | |
394 | { | |
9aba74ce BP |
395 | const uint8_t *buf = buf_; |
396 | const size_t per_line = 16; /* Maximum bytes per line. */ | |
397 | ||
398 | while (size > 0) { | |
399 | size_t start, end, n; | |
400 | size_t i; | |
401 | ||
402 | /* Number of bytes on this line. */ | |
403 | start = ofs % per_line; | |
404 | end = per_line; | |
405 | if (end - start > size) | |
406 | end = start + size; | |
407 | n = end - start; | |
408 | ||
409 | /* Print line. */ | |
34582733 AS |
410 | ds_put_format(ds, "%08"PRIxMAX" ", |
411 | (uintmax_t) ROUND_DOWN(ofs, per_line)); | |
9aba74ce | 412 | for (i = 0; i < start; i++) { |
064af421 | 413 | ds_put_format(ds, " "); |
064af421 | 414 | } |
9aba74ce | 415 | for (; i < end; i++) { |
34582733 | 416 | ds_put_format(ds, "%02x%c", |
9aba74ce BP |
417 | buf[i - start], i == per_line / 2 - 1? '-' : ' '); |
418 | } | |
419 | if (ascii) { | |
420 | for (; i < per_line; i++) | |
421 | ds_put_format(ds, " "); | |
422 | ds_put_format(ds, "|"); | |
423 | for (i = 0; i < start; i++) | |
424 | ds_put_format(ds, " "); | |
425 | for (; i < end; i++) { | |
426 | int c = buf[i - start]; | |
427 | ds_put_char(ds, c >= 32 && c < 127 ? c : '.'); | |
428 | } | |
429 | for (; i < per_line; i++) | |
430 | ds_put_format(ds, " "); | |
431 | ds_put_format(ds, "|"); | |
aaa4b65b BP |
432 | } else { |
433 | ds_chomp(ds, ' '); | |
9aba74ce BP |
434 | } |
435 | ds_put_format(ds, "\n"); | |
064af421 | 436 | |
9aba74ce BP |
437 | ofs += n; |
438 | buf += n; | |
439 | size -= n; | |
064af421 BP |
440 | } |
441 | } | |
442 | ||
443 | int | |
444 | ds_last(const struct ds *ds) | |
445 | { | |
446 | return ds->length > 0 ? (unsigned char) ds->string[ds->length - 1] : EOF; | |
447 | } | |
448 | ||
29100ef2 | 449 | bool |
064af421 BP |
450 | ds_chomp(struct ds *ds, int c) |
451 | { | |
452 | if (ds->length > 0 && ds->string[ds->length - 1] == (char) c) { | |
453 | ds->string[--ds->length] = '\0'; | |
29100ef2 BP |
454 | return true; |
455 | } else { | |
456 | return false; | |
064af421 BP |
457 | } |
458 | } | |
fa44a4a3 RM |
459 | |
460 | void | |
461 | ds_clone(struct ds *dst, struct ds *source) | |
462 | { | |
463 | dst->length = source->length; | |
464 | dst->allocated = dst->length; | |
465 | dst->string = xmalloc(dst->allocated + 1); | |
466 | memcpy(dst->string, source->string, dst->allocated + 1); | |
467 | } |