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