]> git.saurik.com Git - apple/libplatform.git/blob - src/simple/string_io.c
libplatform-220.tar.gz
[apple/libplatform.git] / src / simple / string_io.c
1 /*
2 * Copyright (c) 2005, 2006, 2009 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <mach/mach_init.h>
27 #include <mach/vm_map.h>
28 #include <errno.h>
29
30 #include "os/internal.h"
31 #include "_simple.h"
32 #include "platform/string.h"
33 #include "platform/compat.h"
34
35 #ifndef VM_PAGE_SIZE
36 #define VM_PAGE_SIZE 4096
37 #endif
38
39 #define BUF_SIZE(s) (((BUF *)(s))->end - ((BUF *)(s))->buf + 1)
40 #if DEBUG
41 #define MYBUFSIZE 256
42 #else
43 /* we use a small buffer to minimize stack usage constraints */
44 #define MYBUFSIZE 32
45 #endif
46
47 typedef struct _BUF {
48 char *buf;
49 char *ptr;
50 char *end;
51 int fd;
52 void (*full)(struct _BUF *);
53 } BUF;
54
55 /* flush the buffer */
56 static void
57 _flush(BUF *b)
58 {
59 char *buf = b->buf;
60 ssize_t n = b->ptr - buf;
61 ssize_t w;
62
63 while (n > 0) {
64 w = write(b->fd, buf, n);
65 if (w < 0) {
66 if (errno == EINTR || errno == EAGAIN)
67 continue;
68 break;
69 }
70 n -= w;
71 buf += n;
72 }
73 }
74
75 /* flush the buffer and reset the pointer */
76 static void
77 _flush_reset(BUF *b)
78 {
79 _flush(b);
80 b->ptr = b->buf;
81 }
82
83 /* enlarge the buffer */
84 static void
85 _enlarge(BUF *b)
86 {
87 vm_address_t new;
88 vm_size_t sold, snew;
89 intptr_t diff;
90 kern_return_t kr;
91
92 new = (vm_address_t)(b->end + 1);
93 if(vm_allocate(mach_task_self(), &new, VM_PAGE_SIZE, 0) == 0) {
94 /* page is adjacent */
95 b->end += VM_PAGE_SIZE;
96 return;
97 }
98 sold = BUF_SIZE(b);
99 snew = (sold + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1);
100 if ((kr = vm_allocate(mach_task_self(), &new, snew, 1)) != 0) {
101 __LIBPLATFORM_CLIENT_CRASH__(kr, "Failed to allocate memory for buffer");
102 }
103 diff = new - (vm_address_t)b->buf;
104 memmove((void *)new, b->buf, sold);
105 if((intptr_t)(b->buf) & (VM_PAGE_SIZE - 1)) {
106 sold &= ~(VM_PAGE_SIZE - 1);
107 b->buf = (char *)((intptr_t)(b->buf + VM_PAGE_SIZE) & ~(VM_PAGE_SIZE - 1));
108 b->end = (char *)(new + snew - 1);
109 } else
110 b->end += diff + VM_PAGE_SIZE;
111 if(sold > 0) {
112 vm_deallocate(mach_task_self(), (vm_address_t)b->buf, sold);
113 }
114 b->buf = (char *)new;
115 b->ptr += diff;
116 }
117
118 static inline void put_s(BUF *, _esc_func, const char *);
119 /* output a single character */
120 static inline void
121 put_c(BUF *b, _esc_func esc, unsigned char c)
122 {
123 const char *cp;
124
125 if(esc && (cp = esc(c)) != NULL)
126 put_s(b, NULL, cp);
127 else {
128 if(b->ptr >= b->end)
129 b->full(b);
130 *b->ptr++ = c;
131 }
132 }
133
134 /* output a null-terminated string */
135 static inline void
136 put_s(BUF *b, _esc_func esc, const char *str)
137 {
138 while(*str)
139 put_c(b, esc, *str++);
140 }
141
142 /* output a string of the specified size */
143 static inline void
144 put_n(BUF *b, _esc_func esc, const char *str, ssize_t n)
145 {
146 while(n-- > 0)
147 put_c(b, esc, *str++);
148 }
149
150 /*
151 * Output the signed decimal string representing the number in "in". "width" is
152 * the minimum field width, and "zero" is a boolean value, true for zero padding
153 * (otherwise blank padding).
154 */
155 static void
156 dec(BUF *b, _esc_func esc, long long in, int width, int zero)
157 {
158 char buf[32];
159 char *cp = buf + sizeof(buf);
160 ssize_t pad;
161 int neg = 0;
162 unsigned long long n = (unsigned long long)in;
163
164 if(in < 0) {
165 neg++;
166 width--;
167 n = ~n + 1;
168 }
169 *--cp = 0;
170 if(n) {
171 while(n) {
172 *--cp = (n % 10) + '0';
173 n /= 10;
174 }
175 } else
176 *--cp = '0';
177 if(neg && zero) {
178 put_c(b, esc, '-');
179 neg = 0;
180 }
181 pad = width - strlen(cp);
182 zero = zero ? '0' : ' ';
183 while(pad-- > 0)
184 put_c(b, esc, zero);
185 if(neg)
186 put_c(b, esc, '-');
187 put_s(b, esc, cp);
188 }
189
190 /*
191 * Output the octal string representing the number in "n". "width" is
192 * the minimum field width, and "zero" is a boolean value, true for zero padding
193 * (otherwise blank padding).
194 */
195 static void
196 oct(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
197 {
198 char buf[32];
199 char *cp = buf + sizeof(buf);
200 ssize_t pad;
201
202 *--cp = 0;
203 if (n) {
204 while (n) {
205 *--cp = (n % 8) + '0';
206 n /= 8;
207 }
208 } else {
209 *--cp = '0';
210 }
211 pad = width - strlen(cp);
212 zero = zero ? '0' : ' ';
213 while (pad-- > 0) {
214 put_c(b, esc, zero);
215 }
216 put_s(b, esc, cp);
217 }
218
219 /*
220 * Output the hex string representing the number in "n". "width" is the
221 * minimum field width, and "zero" is a boolean value, true for zero padding
222 * (otherwise blank padding). "upper" is a boolean value, true for upper
223 * case hex characters, lower case otherwise. "p" is a boolean value, true
224 * if 0x should be prepended (for %p), otherwise nothing.
225 */
226 static const char _h[] = "0123456789abcdef";
227 static const char _H[] = "0123456789ABCDEF";
228 static const char _0x[] = "0x";
229
230 static void
231 hex(BUF *b, _esc_func esc, unsigned long long n, int width, int zero, int upper, int p)
232 {
233 char buf[32];
234 char *cp = buf + sizeof(buf);
235 const char *h = upper ? _H : _h;
236
237 *--cp = 0;
238 if(n) {
239 while(n) {
240 *--cp = h[n & 0xf];
241 n >>= 4;
242 }
243 } else
244 *--cp = '0';
245 if(p) {
246 width -= 2;
247 if(zero) {
248 put_s(b, esc, _0x);
249 p = 0;
250 }
251 }
252 width -= strlen(cp);
253 zero = zero ? '0' : ' ';
254 while(width-- > 0)
255 put_c(b, esc, zero);
256 if(p)
257 put_s(b, esc, _0x);
258 put_s(b, esc, cp);
259 }
260
261 /*
262 * Output the unsigned decimal string representing the number in "n". "width"
263 * is the minimum field width, and "zero" is a boolean value, true for zero
264 * padding (otherwise blank padding).
265 */
266 static void
267 udec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
268 {
269 char buf[32];
270 char *cp = buf + sizeof(buf);
271 ssize_t pad;
272
273 *--cp = 0;
274 if(n) {
275 while(n) {
276 *--cp = (n % 10) + '0';
277 n /= 10;
278 }
279 } else
280 *--cp = '0';
281 pad = width - strlen(cp);
282 zero = zero ? '0' : ' ';
283 while(pad-- > 0)
284 put_c(b, esc, zero);
285 put_s(b, esc, cp);
286 }
287
288 /*
289 * Output the unsigned decimal string representing the number in "n", rounded
290 * to the nearest MB, KB or b. "width" is the minimum field width, and "zero"
291 * is a boolean value, true for zero padding (otherwise blank padding).
292 */
293 static void
294 ydec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
295 {
296 if(n >= 10 * (1 << 20)) {
297 n += (1 << 19);
298 udec(b, esc, n >> 20, width, zero);
299 put_s(b, esc, "MB");
300 } else if (n >= 10 * (1 << 10)) {
301 n += (1 << 9);
302 udec(b, esc, n >> 10, width, zero);
303 put_s(b, esc, "KB");
304 } else {
305 udec(b, esc, n, width, zero);
306 put_s(b, esc, "b");
307 }
308 }
309
310 /*
311 * The actual engine for all the _simple_*printf routines.
312 */
313 static void
314 __simple_bprintf(BUF *b, _esc_func esc, const char *fmt, va_list ap)
315 {
316 while(*fmt) {
317 int lflag, zero, width;
318 char *cp;
319 if(!(cp = strchr(fmt, '%'))) {
320 put_s(b, esc, fmt);
321 break;
322 }
323 put_n(b, esc, fmt, cp - fmt);
324 fmt = cp + 1;
325 if(*fmt == '%') {
326 put_c(b, esc, '%');
327 fmt++;
328 continue;
329 }
330 lflag = zero = width = 0;
331 for(;;) {
332 switch(*fmt) {
333 case '0':
334 zero++;
335 fmt++;
336 /* drop through */
337 case '1': case '2': case '3': case '4': case '5':
338 case '6': case '7': case '8': case '9':
339 while(*fmt >= '0' && *fmt <= '9')
340 width = 10 * width + (*fmt++ - '0');
341 continue;
342 case 'c':
343 zero = zero ? '0' : ' ';
344 width--;
345 while(width-- > 0)
346 put_c(b, esc, zero);
347 put_c(b, esc, va_arg(ap, int));
348 break;
349 case 'd': case 'i':
350 switch(lflag) {
351 case 0:
352 dec(b, esc, va_arg(ap, int), width, zero);
353 break;
354 case 1:
355 dec(b, esc, va_arg(ap, long), width, zero);
356 break;
357 default:
358 dec(b, esc, va_arg(ap, long long), width, zero);
359 break;
360 }
361 break;
362 case 'l':
363 lflag++;
364 fmt++;
365 continue;
366 case 'o':
367 switch (lflag) {
368 case 0:
369 oct(b, esc, va_arg(ap, int), width, zero);
370 break;
371 case 1:
372 oct(b, esc, va_arg(ap, long), width, zero);
373 break;
374 default:
375 oct(b, esc, va_arg(ap, long long), width, zero);
376 break;
377 }
378 break;
379 case 'p':
380 hex(b, esc, (unsigned long)va_arg(ap, void *), width, zero, 0, 1);
381 break;
382 case 's':
383 cp = va_arg(ap, char *);
384 cp = cp ? cp : "(null)";
385 width -= strlen(cp);
386 zero = zero ? '0' : ' ';
387 while(width-- > 0)
388 put_c(b, esc, zero);
389 put_s(b, esc, cp);
390 break;
391 case 'u':
392 switch(lflag) {
393 case 0:
394 udec(b, esc, va_arg(ap, unsigned int), width, zero);
395 break;
396 case 1:
397 udec(b, esc, va_arg(ap, unsigned long), width, zero);
398 break;
399 default:
400 udec(b, esc, va_arg(ap, unsigned long long), width, zero);
401 break;
402 }
403 break;
404 case 'X': case 'x':
405 switch(lflag) {
406 case 0:
407 hex(b, esc, va_arg(ap, unsigned int), width, zero,
408 *fmt == 'X', 0);
409 break;
410 case 1:
411 hex(b, esc, va_arg(ap, unsigned long), width, zero,
412 *fmt == 'X', 0);
413 break;
414 default:
415 hex(b, esc, va_arg(ap, unsigned long long), width, zero,
416 *fmt == 'X', 0);
417 break;
418 }
419 break;
420 case 'y':
421 switch(lflag) {
422 case 0:
423 ydec(b, esc, va_arg(ap, unsigned int), width, zero);
424 break;
425 case 1:
426 ydec(b, esc, va_arg(ap, unsigned long), width, zero);
427 break;
428 default:
429 ydec(b, esc, va_arg(ap, unsigned long long), width, zero);
430 break;
431 }
432 break;
433 default:
434 put_c(b, esc, *fmt);
435 break;
436 }
437 break;
438 }
439 fmt++;
440 }
441 }
442
443 /*
444 * A simplified vfprintf variant. The format string is interpreted with
445 * arguments from the va_list, and the results are written to the given
446 * file descriptor.
447 */
448 void
449 _simple_vdprintf(int fd, const char *fmt, va_list ap)
450 {
451 BUF b;
452 char buf[MYBUFSIZE];
453
454 b.buf = buf;
455 b.fd = fd;
456 b.ptr = b.buf;
457 b.end = b.buf + MYBUFSIZE;
458 b.full = _flush_reset;
459 __simple_bprintf(&b, NULL, fmt, ap);
460 _flush(&b);
461 }
462
463 /*
464 * A simplified fprintf variant. The format string is interpreted with
465 * arguments from the variable argument list, and the results are written
466 * to the given file descriptor.
467 */
468 void
469 _simple_dprintf(int fd, const char *fmt, ...)
470 {
471 va_list ap;
472
473 va_start(ap, fmt);
474 _simple_vdprintf(fd, fmt, ap);
475 va_end(ap);
476 }
477
478 /*
479 * A simplified string allocate routine. Pass the opaque pointer to structure
480 * to _simple_*sprintf() routines. Use _simple_string() to retrieve the
481 * current string (the string is guaranteed to be null terminated only on
482 * the call to _simple_string()). Use _simple_sfree() to free the structure
483 * and string memory.
484 */
485 _SIMPLE_STRING
486 _simple_salloc(void)
487 {
488 BUF *b;
489
490 if(vm_allocate(mach_task_self(), (vm_address_t *)&b, VM_PAGE_SIZE, 1))
491 return NULL;
492 b->ptr = b->buf = (char *)b + sizeof(BUF);
493 b->end = (char *)b + VM_PAGE_SIZE - 1;
494 b->full = _enlarge;
495 return (_SIMPLE_STRING)b;
496 }
497
498 /*
499 * The format string is interpreted with arguments from the va_list, and the
500 * results are appended to the string maintained by the opaque structure, as
501 * returned by a previous call to _simple_salloc(). Non-zero is returned on
502 * out-of-memory error.
503 */
504 int
505 _simple_vsprintf(_SIMPLE_STRING b, const char *fmt, va_list ap)
506 {
507 return _simple_vesprintf(b, NULL, fmt, ap);
508 }
509
510 /*
511 * The format string is interpreted with arguments from the variable argument
512 * list, and the results are appended to the string maintained by the opaque
513 * structure, as returned by a previous call to _simple_salloc(). Non-zero is
514 * returned on out-of-memory error.
515 */
516 int
517 _simple_sprintf(_SIMPLE_STRING b, const char *fmt, ...)
518 {
519 va_list ap;
520 int ret;
521
522 va_start(ap, fmt);
523 ret = _simple_vesprintf(b, NULL, fmt, ap);
524 va_end(ap);
525 return ret;
526 }
527
528 /*
529 * Like _simple_vsprintf(), except __esc is a function to call on each
530 * character; the function returns NULL if the character should be passed
531 * as is, otherwise, the returned character string is used instead.
532 */
533 int
534 _simple_vesprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, va_list ap)
535 {
536 __simple_bprintf((BUF *)b, esc, fmt, ap);
537 return 0;
538 }
539
540 /*
541 * Like _simple_sprintf(), except __esc is a function to call on each
542 * character; the function returns NULL if the character should be passed
543 * as is, otherwise, the returned character string is used instead.
544 */
545 int _simple_esprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, ...)
546 {
547 va_list ap;
548 int ret;
549
550 va_start(ap, fmt);
551 ret = _simple_vesprintf(b, esc, fmt, ap);
552 va_end(ap);
553 return ret;
554 }
555
556 /*
557 * Return the null terminated string from the opaque structure, as returned
558 * by a previous call to _simple_salloc().
559 */
560 char *
561 _simple_string(_SIMPLE_STRING b)
562 {
563 *((BUF *)b)->ptr = 0;
564 return ((BUF *)b)->buf;
565 }
566
567 /*
568 * Reposition the pointer to the first null in the buffer. After a call to
569 * _simple_string, the buffer can be modified, and shrunk.
570 */
571 void
572 _simple_sresize(_SIMPLE_STRING b)
573 {
574 ((BUF *)b)->ptr = ((BUF *)b)->buf + strlen(((BUF *)b)->buf);
575 }
576
577 /*
578 * Append the null-terminated string to the string associated with the opaque
579 * structure. Non-zero is returned on out-of-memory error.
580 */
581 int
582 _simple_sappend(_SIMPLE_STRING b, const char *str)
583 {
584 return _simple_esappend(b, NULL, str);
585 }
586
587 /*
588 * Like _simple_sappend(), except __esc is a function to call on each
589 * character; the function returns NULL if the character should be passed
590 * as is, otherwise, the returned character string is used instead.
591 */
592 int _simple_esappend(_SIMPLE_STRING b, _esc_func esc, const char *str)
593 {
594 put_s((BUF *)b, esc, str);
595 return 0;
596 }
597
598 /*
599 * Write the string associated with the opaque structure to the file descriptor.
600 */
601 void
602 _simple_put(_SIMPLE_STRING b, int fd)
603 {
604 ((BUF *)b)->fd = fd;
605 _flush((BUF *)b);
606 }
607
608 /*
609 * Write the string associated with the opaque structure and a trailing newline,
610 * to the file descriptor.
611 */
612 void
613 _simple_putline(_SIMPLE_STRING b, int fd)
614 {
615 ((BUF *)b)->fd = fd;
616 *((BUF *)b)->ptr++ = '\n';
617 _flush((BUF *)b);
618 ((BUF *)b)->ptr--;
619 }
620
621 /*
622 * Free the opaque structure, and the associated string.
623 */
624 void
625 _simple_sfree(_SIMPLE_STRING b)
626 {
627 vm_size_t s;
628
629 if(b == NULL) return;
630 if(((intptr_t)(((BUF *)b)->buf) & (VM_PAGE_SIZE - 1)) == 0) {
631 vm_deallocate(mach_task_self(), (vm_address_t)((BUF *)b)->buf, BUF_SIZE(b));
632 s = VM_PAGE_SIZE;
633 } else {
634 s = ((BUF *)b)->end - (char *)b + 1;
635 }
636 vm_deallocate(mach_task_self(), (vm_address_t)b, s);
637 }