]> git.saurik.com Git - apple/libplatform.git/blame - src/simple/string_io.c
libplatform-177.270.1.tar.gz
[apple/libplatform.git] / src / simple / string_io.c
CommitLineData
ada7c492
A
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)
e45b4692
A
40#if DEBUG
41#define MYBUFSIZE 256
42#else
ada7c492
A
43/* we use a small buffer to minimize stack usage constraints */
44#define MYBUFSIZE 32
e45b4692 45#endif
ada7c492
A
46
47typedef 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 */
56static 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 */
76static void
77_flush_reset(BUF *b)
78{
79 _flush(b);
80 b->ptr = b->buf;
81}
82
83/* enlarge the buffer */
84static 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
118static inline void put_s(BUF *, _esc_func, const char *);
119/* output a single character */
120static inline void
121put_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 */
135static inline void
136put_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 */
143static inline void
144put_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 */
155static void
156dec(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 hex string representing the number in "n". "width" is the
192 * minimum field width, and "zero" is a boolean value, true for zero padding
193 * (otherwise blank padding). "upper" is a boolean value, true for upper
194 * case hex characters, lower case otherwise. "p" is a boolean value, true
195 * if 0x should be prepended (for %p), otherwise nothing.
196 */
197static const char _h[] = "0123456789abcdef";
198static const char _H[] = "0123456789ABCDEF";
199static const char _0x[] = "0x";
200
201static void
202hex(BUF *b, _esc_func esc, unsigned long long n, int width, int zero, int upper, int p)
203{
204 char buf[32];
205 char *cp = buf + sizeof(buf);
206 const char *h = upper ? _H : _h;
207
208 *--cp = 0;
209 if(n) {
210 while(n) {
211 *--cp = h[n & 0xf];
212 n >>= 4;
213 }
214 } else
215 *--cp = '0';
216 if(p) {
217 width -= 2;
218 if(zero) {
219 put_s(b, esc, _0x);
220 p = 0;
221 }
222 }
223 width -= strlen(cp);
224 zero = zero ? '0' : ' ';
225 while(width-- > 0)
226 put_c(b, esc, zero);
227 if(p)
228 put_s(b, esc, _0x);
229 put_s(b, esc, cp);
230}
231
232/*
233 * Output the unsigned decimal string representing the number in "n". "width"
234 * is the minimum field width, and "zero" is a boolean value, true for zero
235 * padding (otherwise blank padding).
236 */
237static void
238udec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
239{
240 char buf[32];
241 char *cp = buf + sizeof(buf);
242 ssize_t pad;
243
244 *--cp = 0;
245 if(n) {
246 while(n) {
247 *--cp = (n % 10) + '0';
248 n /= 10;
249 }
250 } else
251 *--cp = '0';
252 pad = width - strlen(cp);
253 zero = zero ? '0' : ' ';
254 while(pad-- > 0)
255 put_c(b, esc, zero);
256 put_s(b, esc, cp);
257}
258
259/*
260 * Output the unsigned decimal string representing the number in "n", rounded
261 * to the nearest MB, KB or b. "width" is the minimum field width, and "zero"
262 * is a boolean value, true for zero padding (otherwise blank padding).
263 */
264static void
265ydec(BUF *b, _esc_func esc, unsigned long long n, int width, int zero)
266{
267 if(n >= 10 * (1 << 20)) {
268 n += (1 << 19);
269 udec(b, esc, n >> 20, width, zero);
270 put_s(b, esc, "MB");
271 } else if (n >= 10 * (1 << 10)) {
272 n += (1 << 9);
273 udec(b, esc, n >> 10, width, zero);
274 put_s(b, esc, "KB");
275 } else {
276 udec(b, esc, n, width, zero);
277 put_s(b, esc, "b");
278 }
279}
280
281/*
282 * The actual engine for all the _simple_*printf routines.
283 */
284static void
285__simple_bprintf(BUF *b, _esc_func esc, const char *fmt, va_list ap)
286{
287 while(*fmt) {
288 int lflag, zero, width;
289 char *cp;
290 if(!(cp = strchr(fmt, '%'))) {
291 put_s(b, esc, fmt);
292 break;
293 }
294 put_n(b, esc, fmt, cp - fmt);
295 fmt = cp + 1;
296 if(*fmt == '%') {
297 put_c(b, esc, '%');
298 fmt++;
299 continue;
300 }
301 lflag = zero = width = 0;
302 for(;;) {
303 switch(*fmt) {
304 case '0':
305 zero++;
306 fmt++;
307 /* drop through */
308 case '1': case '2': case '3': case '4': case '5':
309 case '6': case '7': case '8': case '9':
310 while(*fmt >= '0' && *fmt <= '9')
311 width = 10 * width + (*fmt++ - '0');
312 continue;
313 case 'c':
314 zero = zero ? '0' : ' ';
315 width--;
316 while(width-- > 0)
317 put_c(b, esc, zero);
318 put_c(b, esc, va_arg(ap, int));
319 break;
320 case 'd': case 'i':
321 switch(lflag) {
322 case 0:
323 dec(b, esc, va_arg(ap, int), width, zero);
324 break;
325 case 1:
326 dec(b, esc, va_arg(ap, long), width, zero);
327 break;
328 default:
329 dec(b, esc, va_arg(ap, long long), width, zero);
330 break;
331 }
332 break;
333 case 'l':
334 lflag++;
335 fmt++;
336 continue;
337 case 'p':
338 hex(b, esc, (unsigned long)va_arg(ap, void *), width, zero, 0, 1);
339 break;
340 case 's':
341 cp = va_arg(ap, char *);
342 cp = cp ? cp : "(null)";
343 width -= strlen(cp);
344 zero = zero ? '0' : ' ';
345 while(width-- > 0)
346 put_c(b, esc, zero);
347 put_s(b, esc, cp);
348 break;
349 case 'u':
350 switch(lflag) {
351 case 0:
352 udec(b, esc, va_arg(ap, unsigned int), width, zero);
353 break;
354 case 1:
355 udec(b, esc, va_arg(ap, unsigned long), width, zero);
356 break;
357 default:
358 udec(b, esc, va_arg(ap, unsigned long long), width, zero);
359 break;
360 }
361 break;
362 case 'X': case 'x':
363 switch(lflag) {
364 case 0:
365 hex(b, esc, va_arg(ap, unsigned int), width, zero,
366 *fmt == 'X', 0);
367 break;
368 case 1:
369 hex(b, esc, va_arg(ap, unsigned long), width, zero,
370 *fmt == 'X', 0);
371 break;
372 default:
373 hex(b, esc, va_arg(ap, unsigned long long), width, zero,
374 *fmt == 'X', 0);
375 break;
376 }
377 break;
378 case 'y':
379 switch(lflag) {
380 case 0:
381 ydec(b, esc, va_arg(ap, unsigned int), width, zero);
382 break;
383 case 1:
384 ydec(b, esc, va_arg(ap, unsigned long), width, zero);
385 break;
386 default:
387 ydec(b, esc, va_arg(ap, unsigned long long), width, zero);
388 break;
389 }
390 break;
391 default:
392 put_c(b, esc, *fmt);
393 break;
394 }
395 break;
396 }
397 fmt++;
398 }
399}
400
401/*
402 * A simplified vfprintf variant. The format string is interpreted with
403 * arguments from the va_list, and the results are written to the given
404 * file descriptor.
405 */
406void
407_simple_vdprintf(int fd, const char *fmt, va_list ap)
408{
409 BUF b;
410 char buf[MYBUFSIZE];
411
412 b.buf = buf;
413 b.fd = fd;
414 b.ptr = b.buf;
415 b.end = b.buf + MYBUFSIZE;
416 b.full = _flush_reset;
417 __simple_bprintf(&b, NULL, fmt, ap);
418 _flush(&b);
419}
420
421/*
422 * A simplified fprintf variant. The format string is interpreted with
423 * arguments from the variable argument list, and the results are written
424 * to the given file descriptor.
425 */
426void
427_simple_dprintf(int fd, const char *fmt, ...)
428{
429 va_list ap;
430
431 va_start(ap, fmt);
432 _simple_vdprintf(fd, fmt, ap);
433 va_end(ap);
434}
435
436/*
437 * A simplified string allocate routine. Pass the opaque pointer to structure
438 * to _simple_*sprintf() routines. Use _simple_string() to retrieve the
439 * current string (the string is guaranteed to be null terminated only on
440 * the call to _simple_string()). Use _simple_sfree() to free the structure
441 * and string memory.
442 */
443_SIMPLE_STRING
444_simple_salloc(void)
445{
ada7c492
A
446 BUF *b;
447
e45b4692
A
448 if(vm_allocate(mach_task_self(), (vm_address_t *)&b, VM_PAGE_SIZE, 1))
449 return NULL;
ada7c492
A
450 b->ptr = b->buf = (char *)b + sizeof(BUF);
451 b->end = (char *)b + VM_PAGE_SIZE - 1;
452 b->full = _enlarge;
453 return (_SIMPLE_STRING)b;
454}
455
456/*
457 * The format string is interpreted with arguments from the va_list, and the
458 * results are appended to the string maintained by the opaque structure, as
e45b4692
A
459 * returned by a previous call to _simple_salloc(). Non-zero is returned on
460 * out-of-memory error.
ada7c492
A
461 */
462int
463_simple_vsprintf(_SIMPLE_STRING b, const char *fmt, va_list ap)
464{
465 return _simple_vesprintf(b, NULL, fmt, ap);
466}
467
468/*
469 * The format string is interpreted with arguments from the variable argument
470 * list, and the results are appended to the string maintained by the opaque
e45b4692
A
471 * structure, as returned by a previous call to _simple_salloc(). Non-zero is
472 * returned on out-of-memory error.
ada7c492
A
473 */
474int
475_simple_sprintf(_SIMPLE_STRING b, const char *fmt, ...)
476{
477 va_list ap;
478 int ret;
479
480 va_start(ap, fmt);
481 ret = _simple_vesprintf(b, NULL, fmt, ap);
482 va_end(ap);
483 return ret;
484}
485
486/*
487 * Like _simple_vsprintf(), except __esc is a function to call on each
488 * character; the function returns NULL if the character should be passed
489 * as is, otherwise, the returned character string is used instead.
490 */
491int
492_simple_vesprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, va_list ap)
493{
494 __simple_bprintf((BUF *)b, esc, fmt, ap);
495 return 0;
496}
497
498/*
499 * Like _simple_sprintf(), except __esc is a function to call on each
500 * character; the function returns NULL if the character should be passed
501 * as is, otherwise, the returned character string is used instead.
502 */
503int _simple_esprintf(_SIMPLE_STRING b, _esc_func esc, const char *fmt, ...)
504{
505 va_list ap;
506 int ret;
507
508 va_start(ap, fmt);
509 ret = _simple_vesprintf(b, esc, fmt, ap);
510 va_end(ap);
511 return ret;
512}
513
514/*
515 * Return the null terminated string from the opaque structure, as returned
516 * by a previous call to _simple_salloc().
517 */
518char *
519_simple_string(_SIMPLE_STRING b)
520{
521 *((BUF *)b)->ptr = 0;
522 return ((BUF *)b)->buf;
523}
524
525/*
526 * Reposition the pointer to the first null in the buffer. After a call to
527 * _simple_string, the buffer can be modified, and shrunk.
528 */
529void
530_simple_sresize(_SIMPLE_STRING b)
531{
532 ((BUF *)b)->ptr = ((BUF *)b)->buf + strlen(((BUF *)b)->buf);
533}
534
535/*
536 * Append the null-terminated string to the string associated with the opaque
e45b4692 537 * structure. Non-zero is returned on out-of-memory error.
ada7c492
A
538 */
539int
540_simple_sappend(_SIMPLE_STRING b, const char *str)
541{
542 return _simple_esappend(b, NULL, str);
543}
544
545/*
546 * Like _simple_sappend(), except __esc is a function to call on each
547 * character; the function returns NULL if the character should be passed
548 * as is, otherwise, the returned character string is used instead.
549 */
550int _simple_esappend(_SIMPLE_STRING b, _esc_func esc, const char *str)
551{
552 put_s((BUF *)b, esc, str);
553 return 0;
554}
555
556/*
557 * Write the string associated with the opaque structure to the file descriptor.
558 */
559void
560_simple_put(_SIMPLE_STRING b, int fd)
561{
562 ((BUF *)b)->fd = fd;
563 _flush((BUF *)b);
564}
565
566/*
567 * Write the string associated with the opaque structure and a trailing newline,
568 * to the file descriptor.
569 */
570void
571_simple_putline(_SIMPLE_STRING b, int fd)
572{
573 ((BUF *)b)->fd = fd;
574 *((BUF *)b)->ptr++ = '\n';
575 _flush((BUF *)b);
576 ((BUF *)b)->ptr--;
577}
578
579/*
580 * Free the opaque structure, and the associated string.
581 */
582void
583_simple_sfree(_SIMPLE_STRING b)
584{
585 vm_size_t s;
586
587 if(b == NULL) return;
588 if(((intptr_t)(((BUF *)b)->buf) & (VM_PAGE_SIZE - 1)) == 0) {
589 vm_deallocate(mach_task_self(), (vm_address_t)((BUF *)b)->buf, BUF_SIZE(b));
590 s = VM_PAGE_SIZE;
591 } else {
592 s = ((BUF *)b)->end - (char *)b + 1;
593 }
594 vm_deallocate(mach_task_self(), (vm_address_t)b, s);
595}