]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
e0edde6f | 2 | * Copyright (c) 2008, 2009, 2010, 2011, 2012 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 | ||
b5d29991 GS |
186 | /* Writes the current time to 'string' based on 'template'. |
187 | * The current time is either localtime or UTC based on 'utc'. */ | |
064af421 | 188 | void |
b5d29991 | 189 | ds_put_strftime(struct ds *ds, const char *template, bool utc) |
064af421 | 190 | { |
b5d29991 GS |
191 | const struct tm *tm; |
192 | time_t now = time_wall(); | |
193 | if (utc) { | |
194 | tm = gmtime(&now); | |
195 | } else { | |
064af421 BP |
196 | tm = localtime(&now); |
197 | } | |
b5d29991 | 198 | |
064af421 BP |
199 | for (;;) { |
200 | size_t avail = ds->string ? ds->allocated - ds->length + 1 : 0; | |
201 | size_t used = strftime(&ds->string[ds->length], avail, template, tm); | |
202 | if (used) { | |
203 | ds->length += used; | |
204 | return; | |
205 | } | |
d295e8e9 | 206 | ds_reserve(ds, ds->length + (avail < 32 ? 64 : 2 * avail)); |
064af421 BP |
207 | } |
208 | } | |
209 | ||
210 | int | |
211 | ds_get_line(struct ds *ds, FILE *file) | |
212 | { | |
213 | ds_clear(ds); | |
214 | for (;;) { | |
215 | int c = getc(file); | |
216 | if (c == EOF) { | |
217 | return ds->length ? 0 : EOF; | |
218 | } else if (c == '\n') { | |
219 | return 0; | |
220 | } else { | |
221 | ds_put_char(ds, c); | |
222 | } | |
223 | } | |
224 | } | |
225 | ||
dd8101bc BP |
226 | /* Reads a line from 'file' into 'ds', clearing anything initially in 'ds'. |
227 | * Deletes comments introduced by "#" and skips lines that contains only white | |
228 | * space (after deleting comments). | |
229 | * | |
230 | * Returns 0 if successful, EOF if no non-blank line was found. */ | |
231 | int | |
232 | ds_get_preprocessed_line(struct ds *ds, FILE *file) | |
233 | { | |
234 | while (!ds_get_line(ds, file)) { | |
235 | char *line = ds_cstr(ds); | |
236 | char *comment; | |
237 | ||
238 | /* Delete comments. */ | |
239 | comment = strchr(line, '#'); | |
240 | if (comment) { | |
241 | *comment = '\0'; | |
242 | } | |
243 | ||
244 | /* Return successfully unless the line is all spaces. */ | |
245 | if (line[strspn(line, " \t\n")] != '\0') { | |
246 | return 0; | |
247 | } | |
248 | } | |
249 | return EOF; | |
250 | } | |
251 | ||
06d7ae7d BP |
252 | /* Reads a line from 'file' into 'ds' and does some preprocessing on it: |
253 | * | |
254 | * - If the line begins with #, prints it on stdout and reads the next line. | |
255 | * | |
256 | * - Otherwise, if the line contains an # somewhere else, strips it and | |
257 | * everything following it (as a comment). | |
258 | * | |
259 | * - If (after comment removal) the line contains only white space, prints | |
260 | * a blank line on stdout and reads the next line. | |
261 | * | |
262 | * - Otherwise, returns the line to the caller. | |
263 | * | |
264 | * This is useful in some of the OVS tests, where we want to check that parsing | |
265 | * and then re-formatting some kind of data does not change it, but we also | |
266 | * want to be able to put comments in the input. | |
267 | * | |
268 | * Returns 0 if successful, EOF if no non-blank line was found. */ | |
269 | int | |
270 | ds_get_test_line(struct ds *ds, FILE *file) | |
271 | { | |
272 | for (;;) { | |
273 | char *s, *comment; | |
274 | int retval; | |
275 | ||
276 | retval = ds_get_line(ds, file); | |
277 | if (retval) { | |
278 | return retval; | |
279 | } | |
280 | ||
281 | s = ds_cstr(ds); | |
282 | if (*s == '#') { | |
283 | puts(s); | |
284 | continue; | |
285 | } | |
286 | ||
287 | comment = strchr(s, '#'); | |
288 | if (comment) { | |
289 | *comment = '\0'; | |
290 | } | |
291 | if (s[strspn(s, " \t\n")] == '\0') { | |
292 | putchar('\n'); | |
293 | continue; | |
294 | } | |
295 | ||
296 | return 0; | |
297 | } | |
298 | } | |
299 | ||
064af421 BP |
300 | char * |
301 | ds_cstr(struct ds *ds) | |
302 | { | |
303 | if (!ds->string) { | |
304 | ds_reserve(ds, 0); | |
305 | } | |
306 | ds->string[ds->length] = '\0'; | |
307 | return ds->string; | |
308 | } | |
309 | ||
5f98eed4 BP |
310 | const char * |
311 | ds_cstr_ro(const struct ds *ds) | |
312 | { | |
ebc56baa | 313 | return ds_cstr(CONST_CAST(struct ds *, ds)); |
5f98eed4 BP |
314 | } |
315 | ||
48718554 BP |
316 | /* Returns a null-terminated string representing the current contents of 'ds', |
317 | * which the caller is expected to free with free(), then clears the contents | |
318 | * of 'ds'. */ | |
319 | char * | |
320 | ds_steal_cstr(struct ds *ds) | |
321 | { | |
322 | char *s = ds_cstr(ds); | |
323 | ds_init(ds); | |
324 | return s; | |
325 | } | |
326 | ||
064af421 BP |
327 | void |
328 | ds_destroy(struct ds *ds) | |
329 | { | |
330 | free(ds->string); | |
331 | } | |
332 | ||
e83fd213 BP |
333 | /* Swaps the content of 'a' and 'b'. */ |
334 | void | |
335 | ds_swap(struct ds *a, struct ds *b) | |
336 | { | |
337 | struct ds temp = *a; | |
338 | *a = *b; | |
339 | *b = temp; | |
340 | } | |
341 | ||
064af421 BP |
342 | /* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per |
343 | * line. Numeric offsets are also included, starting at 'ofs' for the first | |
344 | * byte in 'buf'. If 'ascii' is true then the corresponding ASCII characters | |
345 | * are also rendered alongside. */ | |
346 | void | |
347 | ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, | |
348 | uintptr_t ofs, bool ascii) | |
349 | { | |
350 | const uint8_t *buf = buf_; | |
351 | const size_t per_line = 16; /* Maximum bytes per line. */ | |
352 | ||
353 | while (size > 0) | |
354 | { | |
355 | size_t start, end, n; | |
356 | size_t i; | |
357 | ||
358 | /* Number of bytes on this line. */ | |
359 | start = ofs % per_line; | |
360 | end = per_line; | |
361 | if (end - start > size) | |
362 | end = start + size; | |
363 | n = end - start; | |
364 | ||
365 | /* Print line. */ | |
366 | ds_put_format(ds, "%08jx ", (uintmax_t) ROUND_DOWN(ofs, per_line)); | |
367 | for (i = 0; i < start; i++) | |
368 | ds_put_format(ds, " "); | |
369 | for (; i < end; i++) | |
370 | ds_put_format(ds, "%02hhx%c", | |
371 | buf[i - start], i == per_line / 2 - 1? '-' : ' '); | |
372 | if (ascii) | |
373 | { | |
374 | for (; i < per_line; i++) | |
375 | ds_put_format(ds, " "); | |
376 | ds_put_format(ds, "|"); | |
377 | for (i = 0; i < start; i++) | |
378 | ds_put_format(ds, " "); | |
379 | for (; i < end; i++) { | |
380 | int c = buf[i - start]; | |
381 | ds_put_char(ds, c >= 32 && c < 127 ? c : '.'); | |
382 | } | |
383 | for (; i < per_line; i++) | |
384 | ds_put_format(ds, " "); | |
385 | ds_put_format(ds, "|"); | |
386 | } | |
387 | ds_put_format(ds, "\n"); | |
388 | ||
389 | ofs += n; | |
390 | buf += n; | |
391 | size -= n; | |
392 | } | |
393 | } | |
394 | ||
395 | int | |
396 | ds_last(const struct ds *ds) | |
397 | { | |
398 | return ds->length > 0 ? (unsigned char) ds->string[ds->length - 1] : EOF; | |
399 | } | |
400 | ||
401 | void | |
402 | ds_chomp(struct ds *ds, int c) | |
403 | { | |
404 | if (ds->length > 0 && ds->string[ds->length - 1] == (char) c) { | |
405 | ds->string[--ds->length] = '\0'; | |
406 | } | |
407 | } |