blob: 793f8b19fe4d1635503499742f0b58f5e2bf3373 [file] [log] [blame]
David 'Digit' Turner5c734642010-01-20 12:36:51 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Elliott Hughes41b31792013-01-28 10:36:31 -080029#include <../private/debug_format.h> // Relative path so we can #include this for testing.
Elliott Hugheseababde2012-12-20 18:59:05 -080030
31#include <assert.h>
32#include <errno.h>
33#include <stdarg.h>
34#include <stddef.h>
35#include <stdint.h>
36#include <string.h>
37#include <unistd.h>
38
David 'Digit' Turner5c734642010-01-20 12:36:51 -080039/*** Generic output sink
40 ***/
41
Elliott Hughes18a206c2012-10-29 17:37:13 -070042struct Out {
43 void *opaque;
44 void (*send)(void *opaque, const char *data, int len);
45};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080046
Elliott Hughes18a206c2012-10-29 17:37:13 -070047static void out_send(Out *o, const char *data, size_t len) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080048 o->send(o->opaque, data, (int)len);
49}
50
51static void
52out_send_repeat(Out *o, char ch, int count)
53{
54 char pad[8];
55 const int padSize = (int)sizeof(pad);
56
57 memset(pad, ch, sizeof(pad));
58 while (count > 0) {
59 int avail = count;
60 if (avail > padSize) {
61 avail = padSize;
62 }
63 o->send(o->opaque, pad, avail);
64 count -= avail;
65 }
66}
67
68/* forward declaration */
Elliott Hughes18a206c2012-10-29 17:37:13 -070069static void out_vformat(Out* o, const char* format, va_list args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080070
71/*** Bounded buffer output
72 ***/
73
Elliott Hughes18a206c2012-10-29 17:37:13 -070074struct BufOut {
75 Out out[1];
76 char *buffer;
77 char *pos;
78 char *end;
79 int total;
80};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080081
Elliott Hughes18a206c2012-10-29 17:37:13 -070082static void buf_out_send(void *opaque, const char *data, int len) {
83 BufOut *bo = reinterpret_cast<BufOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080084
Elliott Hughes18a206c2012-10-29 17:37:13 -070085 if (len < 0) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080086 len = strlen(data);
Elliott Hughes18a206c2012-10-29 17:37:13 -070087 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -080088
89 bo->total += len;
90
91 while (len > 0) {
92 int avail = bo->end - bo->pos;
93 if (avail == 0)
94 break;
95 if (avail > len)
96 avail = len;
97 memcpy(bo->pos, data, avail);
98 bo->pos += avail;
99 bo->pos[0] = '\0';
100 len -= avail;
101 }
102}
103
104static Out*
105buf_out_init(BufOut *bo, char *buffer, size_t size)
106{
107 if (size == 0)
108 return NULL;
109
110 bo->out->opaque = bo;
111 bo->out->send = buf_out_send;
112 bo->buffer = buffer;
113 bo->end = buffer + size - 1;
114 bo->pos = bo->buffer;
115 bo->pos[0] = '\0';
116 bo->total = 0;
117
118 return bo->out;
119}
120
121static int
122buf_out_length(BufOut *bo)
123{
124 return bo->total;
125}
126
127static int
Elliott Hughes41b31792013-01-28 10:36:31 -0800128vformat_buffer(char *buff, size_t buf_size, const char *format, va_list args)
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800129{
130 BufOut bo;
131 Out *out;
132
Elliott Hughes41b31792013-01-28 10:36:31 -0800133 out = buf_out_init(&bo, buff, buf_size);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800134 if (out == NULL)
135 return 0;
136
137 out_vformat(out, format, args);
138
139 return buf_out_length(&bo);
140}
141
Elliott Hughes1e980b62013-01-17 18:36:06 -0800142int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
143 va_list args;
144 va_start(args, format);
145 int result = vformat_buffer(buffer, buffer_size, format, args);
146 va_end(args);
147 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800148}
149
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800150
151/*** File descriptor output
152 ***/
153
Elliott Hughes18a206c2012-10-29 17:37:13 -0700154struct FdOut {
155 Out out[1];
156 int fd;
157 int total;
158};
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800159
160static void
161fd_out_send(void *opaque, const char *data, int len)
162{
Elliott Hughes1e980b62013-01-17 18:36:06 -0800163 FdOut *fdo = reinterpret_cast<FdOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800164
165 if (len < 0)
166 len = strlen(data);
167
168 while (len > 0) {
169 int ret = write(fdo->fd, data, len);
170 if (ret < 0) {
171 if (errno == EINTR)
172 continue;
173 break;
174 }
175 data += ret;
176 len -= ret;
177 fdo->total += ret;
178 }
179}
180
181static Out*
182fd_out_init(FdOut *fdo, int fd)
183{
184 fdo->out->opaque = fdo;
185 fdo->out->send = fd_out_send;
186 fdo->fd = fd;
187 fdo->total = 0;
188
189 return fdo->out;
190}
191
192static int
193fd_out_length(FdOut *fdo)
194{
195 return fdo->total;
196}
197
198
Elliott Hughes1e980b62013-01-17 18:36:06 -0800199int __libc_format_fd(int fd, const char* format, ...) {
200 FdOut fdo;
201 Out* out = fd_out_init(&fdo, fd);
202 if (out == NULL) {
203 return 0;
204 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800205
Elliott Hughes1e980b62013-01-17 18:36:06 -0800206 va_list args;
207 va_start(args, format);
208 out_vformat(out, format, args);
209 va_end(args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800210
Elliott Hughes1e980b62013-01-17 18:36:06 -0800211 return fd_out_length(&fdo);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800212}
213
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800214/*** Log output
215 ***/
216
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800217#include <unistd.h>
218#include <fcntl.h>
219#include <sys/uio.h>
220
Elliott Hughes1e980b62013-01-17 18:36:06 -0800221int __libc_format_log_va_list(int priority, const char* tag, const char* fmt, va_list args) {
222 char buf[1024];
223 int result = vformat_buffer(buf, sizeof buf, fmt, args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800224
Elliott Hughes1e980b62013-01-17 18:36:06 -0800225 static int log_fd = -1;
226 if (log_fd == -1) {
227 log_fd = open("/dev/log/main", O_WRONLY);
228 if (log_fd == -1) {
229 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800230 }
Elliott Hughes1e980b62013-01-17 18:36:06 -0800231 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800232
Elliott Hughes1e980b62013-01-17 18:36:06 -0800233 struct iovec vec[3];
234 vec[0].iov_base = (unsigned char *) &priority;
235 vec[0].iov_len = 1;
236 vec[1].iov_base = (void *) tag;
237 vec[1].iov_len = strlen(tag) + 1;
238 vec[2].iov_base = (void *) buf;
239 vec[2].iov_len = strlen(buf) + 1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800240
Elliott Hughes1e980b62013-01-17 18:36:06 -0800241 TEMP_FAILURE_RETRY(writev(log_fd, vec, 3));
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800242
Elliott Hughes1e980b62013-01-17 18:36:06 -0800243 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800244}
245
Elliott Hughes1e980b62013-01-17 18:36:06 -0800246int __libc_format_log(int priority, const char* tag, const char* format, ...) {
247 va_list args;
248 va_start(args, format);
249 int result = __libc_format_log_va_list(priority, tag, format, args);
250 va_end(args);
251 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800252}
253
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800254/*** formatted output implementation
255 ***/
256
257/* Parse a decimal string from 'format + *ppos',
258 * return the value, and writes the new position past
259 * the decimal string in '*ppos' on exit.
260 *
261 * NOTE: Does *not* handle a sign prefix.
262 */
263static unsigned
264parse_decimal(const char *format, int *ppos)
265{
266 const char* p = format + *ppos;
267 unsigned result = 0;
268
269 for (;;) {
270 int ch = *p;
271 unsigned d = (unsigned)(ch - '0');
272
273 if (d >= 10U)
274 break;
275
276 result = result*10 + d;
277 p++;
278 }
279 *ppos = p - format;
280 return result;
281}
282
Elliott Hugheseababde2012-12-20 18:59:05 -0800283// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
284// Assumes that buf_size > 0.
Elliott Hughes41b31792013-01-28 10:36:31 -0800285static void format_unsigned(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
Elliott Hugheseababde2012-12-20 18:59:05 -0800286 char* p = buf;
287 char* end = buf + buf_size - 1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800288
Elliott Hugheseababde2012-12-20 18:59:05 -0800289 // Generate digit string in reverse order.
290 while (value) {
291 unsigned d = value % base;
292 value /= base;
293 if (p != end) {
294 char ch;
295 if (d < 10) {
296 ch = '0' + d;
297 } else {
298 ch = (caps ? 'A' : 'a') + (d - 10);
299 }
300 *p++ = ch;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800301 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800302 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800303
Elliott Hugheseababde2012-12-20 18:59:05 -0800304 // Special case for 0.
305 if (p == buf) {
306 if (p != end) {
307 *p++ = '0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800308 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800309 }
310 *p = '\0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800311
Elliott Hugheseababde2012-12-20 18:59:05 -0800312 // Reverse digit string in-place.
313 size_t length = p - buf;
314 for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
315 char ch = buf[i];
316 buf[i] = buf[j];
317 buf[j] = ch;
318 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800319}
320
Elliott Hughes41b31792013-01-28 10:36:31 -0800321static void format_integer(char* buf, size_t buf_size, uint64_t value, char conversion) {
322 // Decode the conversion specifier.
323 int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
324 int base = 10;
325 if (conversion == 'x' || conversion == 'X') {
326 base = 16;
327 } else if (conversion == 'o') {
328 base = 8;
329 }
330 bool caps = (conversion == 'X');
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800331
Elliott Hughes41b31792013-01-28 10:36:31 -0800332 if (is_signed && static_cast<int64_t>(value) < 0) {
333 buf[0] = '-';
334 buf += 1;
335 buf_size -= 1;
336 value = static_cast<uint64_t>(-static_cast<int64_t>(value));
337 }
338 format_unsigned(buf, buf_size, value, base, caps);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800339}
340
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800341/* Perform formatted output to an output target 'o' */
342static void
343out_vformat(Out *o, const char *format, va_list args)
344{
Andy McFaddenec92af82011-07-29 12:46:34 -0700345 int nn = 0;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800346
347 for (;;) {
Andy McFaddenec92af82011-07-29 12:46:34 -0700348 int mm;
349 int padZero = 0;
350 int padLeft = 0;
351 char sign = '\0';
352 int width = -1;
353 int prec = -1;
354 size_t bytelen = sizeof(int);
Andy McFaddenec92af82011-07-29 12:46:34 -0700355 int slen;
356 char buffer[32]; /* temporary buffer used to format numbers */
357
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800358 char c;
359
360 /* first, find all characters that are not 0 or '%' */
361 /* then send them to the output directly */
362 mm = nn;
363 do {
364 c = format[mm];
365 if (c == '\0' || c == '%')
366 break;
367 mm++;
368 } while (1);
369
370 if (mm > nn) {
371 out_send(o, format+nn, mm-nn);
372 nn = mm;
373 }
374
375 /* is this it ? then exit */
376 if (c == '\0')
377 break;
378
379 /* nope, we are at a '%' modifier */
380 nn++; // skip it
381
382 /* parse flags */
383 for (;;) {
384 c = format[nn++];
385 if (c == '\0') { /* single trailing '%' ? */
386 c = '%';
387 out_send(o, &c, 1);
388 return;
389 }
390 else if (c == '0') {
391 padZero = 1;
392 continue;
393 }
394 else if (c == '-') {
395 padLeft = 1;
396 continue;
397 }
398 else if (c == ' ' || c == '+') {
399 sign = c;
400 continue;
401 }
402 break;
403 }
404
405 /* parse field width */
406 if ((c >= '0' && c <= '9')) {
407 nn --;
408 width = (int)parse_decimal(format, &nn);
409 c = format[nn++];
410 }
411
412 /* parse precision */
413 if (c == '.') {
414 prec = (int)parse_decimal(format, &nn);
415 c = format[nn++];
416 }
417
418 /* length modifier */
419 switch (c) {
420 case 'h':
421 bytelen = sizeof(short);
422 if (format[nn] == 'h') {
423 bytelen = sizeof(char);
424 nn += 1;
425 }
426 c = format[nn++];
427 break;
428 case 'l':
429 bytelen = sizeof(long);
430 if (format[nn] == 'l') {
431 bytelen = sizeof(long long);
432 nn += 1;
433 }
434 c = format[nn++];
435 break;
436 case 'z':
437 bytelen = sizeof(size_t);
438 c = format[nn++];
439 break;
440 case 't':
441 bytelen = sizeof(ptrdiff_t);
442 c = format[nn++];
443 break;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800444 default:
445 ;
446 }
447
448 /* conversion specifier */
Elliott Hughes41b31792013-01-28 10:36:31 -0800449 const char* str = buffer;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800450 if (c == 's') {
451 /* string */
452 str = va_arg(args, const char*);
Elliott Hughes239e7a02013-01-25 17:13:45 -0800453 if (str == NULL) {
454 str = "(null)";
455 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800456 } else if (c == 'c') {
457 /* character */
458 /* NOTE: char is promoted to int when passed through the stack */
459 buffer[0] = (char) va_arg(args, int);
460 buffer[1] = '\0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800461 } else if (c == 'p') {
Andy McFaddenec92af82011-07-29 12:46:34 -0700462 uint64_t value = (uintptr_t) va_arg(args, void*);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800463 buffer[0] = '0';
464 buffer[1] = 'x';
Elliott Hughes41b31792013-01-28 10:36:31 -0800465 format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
466 } else if (c == 'd' || c == 'i' || c == 'o' || c == 'x' || c == 'X') {
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800467 /* integers - first read value from stack */
468 uint64_t value;
Elliott Hughes41b31792013-01-28 10:36:31 -0800469 int is_signed = (c == 'd' || c == 'i' || c == 'o');
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800470
471 /* NOTE: int8_t and int16_t are promoted to int when passed
472 * through the stack
473 */
474 switch (bytelen) {
475 case 1: value = (uint8_t) va_arg(args, int); break;
476 case 2: value = (uint16_t) va_arg(args, int); break;
477 case 4: value = va_arg(args, uint32_t); break;
478 case 8: value = va_arg(args, uint64_t); break;
479 default: return; /* should not happen */
480 }
481
482 /* sign extension, if needed */
Elliott Hughes41b31792013-01-28 10:36:31 -0800483 if (is_signed) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800484 int shift = 64 - 8*bytelen;
485 value = (uint64_t)(((int64_t)(value << shift)) >> shift);
486 }
487
488 /* format the number properly into our buffer */
Elliott Hughes41b31792013-01-28 10:36:31 -0800489 format_integer(buffer, sizeof(buffer), value, c);
490 } else if (c == '%') {
491 buffer[0] = '%';
492 buffer[1] = '\0';
493 } else {
494 __assert(__FILE__, __LINE__, "conversion specifier unsupported");
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800495 }
496
497 /* if we are here, 'str' points to the content that must be
498 * outputted. handle padding and alignment now */
499
500 slen = strlen(str);
501
Elliott Hughes18a206c2012-10-29 17:37:13 -0700502 if (sign != '\0' || prec != -1) {
503 __assert(__FILE__, __LINE__, "sign/precision unsupported");
504 }
505
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800506 if (slen < width && !padLeft) {
507 char padChar = padZero ? '0' : ' ';
508 out_send_repeat(o, padChar, width - slen);
509 }
510
511 out_send(o, str, slen);
512
513 if (slen < width && padLeft) {
514 char padChar = padZero ? '0' : ' ';
515 out_send_repeat(o, padChar, width - slen);
516 }
517 }
518}