00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "ruby/config.h"
00012 #include "addr2line.h"
00013
00014 #include <stdio.h>
00015 #include <errno.h>
00016
00017 #ifdef USE_ELF
00018
00019 #ifdef __OpenBSD__
00020 #include <elf_abi.h>
00021 #else
00022 #include <elf.h>
00023 #endif
00024 #include <fcntl.h>
00025 #include <limits.h>
00026 #include <stdio.h>
00027 #include <stdint.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <sys/mman.h>
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #include <unistd.h>
00034
00035
00036 #ifdef __GNUC__
00037 # ifndef atarist
00038 # ifndef alloca
00039 # define alloca __builtin_alloca
00040 # endif
00041 # endif
00042 #else
00043 # ifdef HAVE_ALLOCA_H
00044 # include <alloca.h>
00045 # else
00046 # ifdef _AIX
00047 #pragma alloca
00048 # else
00049 # ifndef alloca
00050 void *alloca();
00051 # endif
00052 # endif
00053 # endif
00054 #endif
00055
00056 #ifdef HAVE_DL_ITERATE_PHDR
00057 # ifndef _GNU_SOURCE
00058 # define _GNU_SOURCE
00059 # endif
00060 # include <link.h>
00061 #endif
00062
00063 #define DW_LNS_copy 0x01
00064 #define DW_LNS_advance_pc 0x02
00065 #define DW_LNS_advance_line 0x03
00066 #define DW_LNS_set_file 0x04
00067 #define DW_LNS_set_column 0x05
00068 #define DW_LNS_negate_stmt 0x06
00069 #define DW_LNS_set_basic_block 0x07
00070 #define DW_LNS_const_add_pc 0x08
00071 #define DW_LNS_fixed_advance_pc 0x09
00072 #define DW_LNS_set_prologue_end 0x0a
00073 #define DW_LNS_set_epilogue_begin 0x0b
00074 #define DW_LNS_set_isa 0x0c
00075
00076
00077 #define DW_LNE_end_sequence 0x01
00078 #define DW_LNE_set_address 0x02
00079 #define DW_LNE_define_file 0x03
00080 #define DW_LNE_set_discriminator 0x04
00081
00082 #ifndef ElfW
00083 # if SIZEOF_VOIDP == 8
00084 # define ElfW(x) Elf64##_##x
00085 # else
00086 # define ElfW(x) Elf32##_##x
00087 # endif
00088 #endif
00089 #ifndef PATH_MAX
00090 #define PATH_MAX 4096
00091 #endif
00092
00093 int kprintf(const char *fmt, ...);
00094
00095 typedef struct {
00096 const char *dirname;
00097 const char *filename;
00098 int line;
00099
00100 int fd;
00101 void *mapped;
00102 size_t mapped_size;
00103 unsigned long base_addr;
00104 } line_info_t;
00105
00106
00107 static char binary_filename[PATH_MAX];
00108
00109 static unsigned long
00110 uleb128(char **p)
00111 {
00112 unsigned long r = 0;
00113 int s = 0;
00114 for (;;) {
00115 unsigned char b = *(unsigned char *)(*p)++;
00116 if (b < 0x80) {
00117 r += (unsigned long)b << s;
00118 break;
00119 }
00120 r += (b & 0x7f) << s;
00121 s += 7;
00122 }
00123 return r;
00124 }
00125
00126 static long
00127 sleb128(char **p)
00128 {
00129 long r = 0;
00130 int s = 0;
00131 for (;;) {
00132 unsigned char b = *(unsigned char *)(*p)++;
00133 if (b < 0x80) {
00134 if (b & 0x40) {
00135 r -= (0x80 - b) << s;
00136 }
00137 else {
00138 r += (b & 0x3f) << s;
00139 }
00140 break;
00141 }
00142 r += (b & 0x7f) << s;
00143 s += 7;
00144 }
00145 return r;
00146 }
00147
00148 static const char *
00149 get_nth_dirname(unsigned long dir, char *p)
00150 {
00151 if (!dir--) {
00152 return "";
00153 }
00154 while (dir--) {
00155 while (*p) p++;
00156 p++;
00157 if (!*p) {
00158 kprintf("Unexpected directory number %lu in %s\n",
00159 dir, binary_filename);
00160 return "";
00161 }
00162 }
00163 return p;
00164 }
00165
00166 static void
00167 fill_filename(int file, char *include_directories, char *filenames,
00168 line_info_t *line)
00169 {
00170 int i;
00171 char *p = filenames;
00172 char *filename;
00173 unsigned long dir;
00174 for (i = 1; i <= file; i++) {
00175 filename = p;
00176 if (!*p) {
00177
00178 kprintf("Unexpected file number %d in %s\n",
00179 file, binary_filename);
00180 return;
00181 }
00182 while (*p) p++;
00183 p++;
00184 dir = uleb128(&p);
00185
00186 uleb128(&p);
00187
00188 uleb128(&p);
00189
00190 if (i == file) {
00191 line->filename = filename;
00192 line->dirname = get_nth_dirname(dir, include_directories);
00193 }
00194 }
00195 }
00196
00197 static int
00198 get_path_from_symbol(const char *symbol, const char **p, size_t *len)
00199 {
00200 if (symbol[0] == '0') {
00201
00202 *p = strchr(symbol, '/');
00203 if (*p == NULL) return 0;
00204 *len = strlen(*p);
00205 }
00206 else {
00207
00208 const char *q;
00209 *p = symbol;
00210 q = strchr(symbol, '(');
00211 if (q == NULL) return 0;
00212 *len = q - symbol;
00213 }
00214 return 1;
00215 }
00216
00217 static void
00218 fill_line(int num_traces, void **traces,
00219 unsigned long addr, int file, int line,
00220 char *include_directories, char *filenames, line_info_t *lines)
00221 {
00222 int i;
00223 for (i = 0; i < num_traces; i++) {
00224 unsigned long a = (unsigned long)traces[i] - lines[i].base_addr;
00225
00226
00227 if (addr < a && a < addr + 100) {
00228 fill_filename(file, include_directories, filenames, &lines[i]);
00229 lines[i].line = line;
00230 }
00231 }
00232 }
00233
00234 static void
00235 parse_debug_line_cu(int num_traces, void **traces,
00236 char **debug_line, line_info_t *lines)
00237 {
00238 char *p, *cu_end, *cu_start, *include_directories, *filenames;
00239 unsigned long unit_length;
00240 int default_is_stmt, line_base;
00241 unsigned int header_length, minimum_instruction_length, line_range,
00242 opcode_base;
00243
00244
00245
00246 unsigned long addr = 0;
00247 unsigned int file = 1;
00248 unsigned int line = 1;
00249
00250 int is_stmt;
00251
00252
00253
00254
00255
00256
00257 p = *debug_line;
00258
00259 unit_length = *(unsigned int *)p;
00260 p += sizeof(unsigned int);
00261 if (unit_length == 0xffffffff) {
00262 unit_length = *(unsigned long *)p;
00263 p += sizeof(unsigned long);
00264 }
00265
00266 cu_end = p + unit_length;
00267
00268
00269 p += 2;
00270
00271 header_length = *(unsigned int *)p;
00272 p += sizeof(unsigned int);
00273
00274 cu_start = p + header_length;
00275
00276 minimum_instruction_length = *(unsigned char *)p;
00277 p++;
00278
00279 is_stmt = default_is_stmt = *(unsigned char *)p;
00280 p++;
00281
00282 line_base = *(char *)p;
00283 p++;
00284
00285 line_range = *(unsigned char *)p;
00286 p++;
00287
00288 opcode_base = *(unsigned char *)p;
00289 p++;
00290
00291
00292 p += opcode_base - 1;
00293
00294 include_directories = p;
00295
00296
00297 while (*p) {
00298 while (*p) p++;
00299 p++;
00300 }
00301 p++;
00302
00303 filenames = p;
00304
00305 p = cu_start;
00306
00307 #define FILL_LINE() \
00308 do { \
00309 fill_line(num_traces, traces, addr, file, line, \
00310 include_directories, filenames, lines); \
00311 \
00312 } while (0)
00313
00314 while (p < cu_end) {
00315 unsigned long a;
00316 unsigned char op = *p++;
00317 switch (op) {
00318 case DW_LNS_copy:
00319 FILL_LINE();
00320 break;
00321 case DW_LNS_advance_pc:
00322 a = uleb128(&p);
00323 addr += a;
00324 break;
00325 case DW_LNS_advance_line: {
00326 long a = sleb128(&p);
00327 line += a;
00328 break;
00329 }
00330 case DW_LNS_set_file:
00331 file = (unsigned int)uleb128(&p);
00332 break;
00333 case DW_LNS_set_column:
00334 (void)uleb128(&p);
00335 break;
00336 case DW_LNS_negate_stmt:
00337 is_stmt = !is_stmt;
00338 break;
00339 case DW_LNS_set_basic_block:
00340
00341 break;
00342 case DW_LNS_const_add_pc:
00343 a = ((255 - opcode_base) / line_range) *
00344 minimum_instruction_length;
00345 addr += a;
00346 break;
00347 case DW_LNS_fixed_advance_pc:
00348 a = *(unsigned char *)p++;
00349 addr += a;
00350 break;
00351 case DW_LNS_set_prologue_end:
00352
00353 break;
00354 case DW_LNS_set_epilogue_begin:
00355
00356 break;
00357 case DW_LNS_set_isa:
00358 (void)uleb128(&p);
00359 break;
00360 case 0:
00361 a = *(unsigned char *)p++;
00362 op = *p++;
00363 switch (op) {
00364 case DW_LNE_end_sequence:
00365
00366 FILL_LINE();
00367 addr = 0;
00368 file = 1;
00369 line = 1;
00370
00371 is_stmt = default_is_stmt;
00372
00373
00374 break;
00375 case DW_LNE_set_address:
00376 addr = *(unsigned long *)p;
00377 p += sizeof(unsigned long);
00378 break;
00379 case DW_LNE_define_file:
00380 kprintf("Unsupported operation in %s\n",
00381 binary_filename);
00382 break;
00383 case DW_LNE_set_discriminator:
00384
00385 uleb128(&p);
00386 break;
00387 default:
00388 kprintf("Unknown extended opcode: %d in %s\n",
00389 op, binary_filename);
00390 }
00391 break;
00392 default: {
00393 unsigned long addr_incr;
00394 unsigned long line_incr;
00395 a = op - opcode_base;
00396 addr_incr = (a / line_range) * minimum_instruction_length;
00397 line_incr = line_base + (a % line_range);
00398 addr += (unsigned int)addr_incr;
00399 line += (unsigned int)line_incr;
00400 FILL_LINE();
00401 }
00402 }
00403 }
00404 *debug_line = p;
00405 }
00406
00407 static void
00408 parse_debug_line(int num_traces, void **traces,
00409 char *debug_line, unsigned long size, line_info_t *lines)
00410 {
00411 char *debug_line_end = debug_line + size;
00412 while (debug_line < debug_line_end) {
00413 parse_debug_line_cu(num_traces, traces, &debug_line, lines);
00414 }
00415 if (debug_line != debug_line_end) {
00416 kprintf("Unexpected size of .debug_line in %s\n",
00417 binary_filename);
00418 }
00419 }
00420
00421
00422 static void
00423 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink,
00424 line_info_t *current_line, line_info_t *lines);
00425
00426 static void
00427 follow_debuglink(char *debuglink, int num_traces, void **traces, char **syms,
00428 line_info_t *current_line, line_info_t *lines)
00429 {
00430
00431
00432
00433 static const char global_debug_dir[] = "/usr/lib/debug";
00434 char *p, *subdir;
00435
00436 p = strrchr(binary_filename, '/');
00437 if (!p) {
00438 return;
00439 }
00440 p[1] = '\0';
00441
00442 subdir = (char *)alloca(strlen(binary_filename) + 1);
00443 strcpy(subdir, binary_filename);
00444 strcpy(binary_filename, global_debug_dir);
00445 strncat(binary_filename, subdir,
00446 PATH_MAX - strlen(binary_filename) - 1);
00447 strncat(binary_filename, debuglink,
00448 PATH_MAX - strlen(binary_filename) - 1);
00449
00450 munmap(current_line->mapped, current_line->mapped_size);
00451 close(current_line->fd);
00452 fill_lines(num_traces, traces, syms, 0, current_line, lines);
00453 }
00454
00455
00456 static void
00457 fill_lines(int num_traces, void **traces, char **syms, int check_debuglink,
00458 line_info_t *current_line, line_info_t *lines)
00459 {
00460 int i;
00461 char *shstr;
00462 char *section_name;
00463 ElfW(Ehdr) *ehdr;
00464 ElfW(Shdr) *shdr, *shstr_shdr;
00465 ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL;
00466 int fd;
00467 off_t filesize;
00468 char *file;
00469
00470 fd = open(binary_filename, O_RDONLY);
00471 if (fd < 0) {
00472 return;
00473 }
00474 filesize = lseek(fd, 0, SEEK_END);
00475 if (filesize < 0) {
00476 int e = errno;
00477 close(fd);
00478 kprintf("lseek: %s\n", strerror(e));
00479 return;
00480 }
00481 #if SIZEOF_OFF_T > SIZEOF_SIZE_T
00482 if (filesize > (off_t)SIZE_MAX) {
00483 close(fd);
00484 kprintf("Too large file %s\n", binary_filename);
00485 return;
00486 }
00487 #endif
00488 lseek(fd, 0, SEEK_SET);
00489
00490 file = (char *)mmap(NULL, (size_t)filesize, PROT_READ, MAP_SHARED, fd, 0);
00491 if (file == MAP_FAILED) {
00492 int e = errno;
00493 close(fd);
00494 kprintf("mmap: %s\n", strerror(e));
00495 return;
00496 }
00497
00498 ehdr = (ElfW(Ehdr) *)file;
00499 if (memcmp(ehdr->e_ident, "\177ELF", 4) != 0) {
00500
00501
00502
00503
00504 close(fd);
00505 return;
00506 }
00507
00508 current_line->fd = fd;
00509 current_line->mapped = file;
00510 current_line->mapped_size = (size_t)filesize;
00511
00512 for (i = 0; i < num_traces; i++) {
00513 const char *path;
00514 size_t len;
00515 if (get_path_from_symbol(syms[i], &path, &len) &&
00516 !strncmp(path, binary_filename, len)) {
00517 lines[i].line = -1;
00518 }
00519 }
00520
00521 shdr = (ElfW(Shdr) *)(file + ehdr->e_shoff);
00522
00523 shstr_shdr = shdr + ehdr->e_shstrndx;
00524 shstr = file + shstr_shdr->sh_offset;
00525
00526 for (i = 0; i < ehdr->e_shnum; i++) {
00527 section_name = shstr + shdr[i].sh_name;
00528 if (!strcmp(section_name, ".debug_line")) {
00529 debug_line_shdr = shdr + i;
00530 break;
00531 } else if (!strcmp(section_name, ".gnu_debuglink")) {
00532 gnu_debuglink_shdr = shdr + i;
00533 }
00534 }
00535
00536 if (!debug_line_shdr) {
00537
00538
00539 if (gnu_debuglink_shdr && check_debuglink) {
00540 follow_debuglink(file + gnu_debuglink_shdr->sh_offset,
00541 num_traces, traces, syms,
00542 current_line, lines);
00543 }
00544 return;
00545 }
00546
00547 parse_debug_line(num_traces, traces,
00548 file + debug_line_shdr->sh_offset,
00549 debug_line_shdr->sh_size,
00550 lines);
00551 }
00552
00553 #ifdef HAVE_DL_ITERATE_PHDR
00554
00555 typedef struct {
00556 int num_traces;
00557 char **syms;
00558 line_info_t *lines;
00559 } fill_base_addr_state_t;
00560
00561 static int
00562 fill_base_addr(struct dl_phdr_info *info, size_t size, void *data)
00563 {
00564 int i;
00565 fill_base_addr_state_t *st = (fill_base_addr_state_t *)data;
00566 for (i = 0; i < st->num_traces; i++) {
00567 const char *path;
00568 size_t len;
00569 size_t name_len = strlen(info->dlpi_name);
00570
00571 if (get_path_from_symbol(st->syms[i], &path, &len) &&
00572 (len == name_len || (len > name_len && path[len-name_len-1] == '/')) &&
00573 !strncmp(path+len-name_len, info->dlpi_name, name_len)) {
00574 st->lines[i].base_addr = info->dlpi_addr;
00575 }
00576 }
00577 return 0;
00578 }
00579
00580 #endif
00581
00582 void
00583 rb_dump_backtrace_with_lines(int num_traces, void **trace, char **syms)
00584 {
00585 int i;
00586
00587 line_info_t *lines = (line_info_t *)calloc(num_traces,
00588 sizeof(line_info_t));
00589
00590
00591
00592 #ifdef HAVE_DL_ITERATE_PHDR
00593 fill_base_addr_state_t fill_base_addr_state;
00594
00595 fill_base_addr_state.num_traces = num_traces;
00596 fill_base_addr_state.syms = syms;
00597 fill_base_addr_state.lines = lines;
00598
00599 dl_iterate_phdr(fill_base_addr, &fill_base_addr_state);
00600 #endif
00601
00602 for (i = 0; i < num_traces; i++) {
00603 const char *path;
00604 size_t len;
00605 if (lines[i].line) {
00606 continue;
00607 }
00608
00609 if (!get_path_from_symbol(syms[i], &path, &len)) {
00610 continue;
00611 }
00612
00613 strncpy(binary_filename, path, len);
00614 binary_filename[len] = '\0';
00615
00616 fill_lines(num_traces, trace, syms, 1, &lines[i], lines);
00617 }
00618
00619 for (i = 0; i < num_traces; i++) {
00620 line_info_t *line = &lines[i];
00621
00622 if (line->line > 0) {
00623 if (line->filename) {
00624 if (line->dirname && line->dirname[0]) {
00625 kprintf("%s %s/%s:%d\n", syms[i], line->dirname, line->filename, line->line);
00626 }
00627 else {
00628 kprintf("%s %s:%d\n", syms[i], line->filename, line->line);
00629 }
00630 } else {
00631 kprintf("%s ???:%d\n", syms[i], line->line);
00632 }
00633 } else {
00634 kprintf("%s\n", syms[i]);
00635 }
00636 }
00637
00638 for (i = 0; i < num_traces; i++) {
00639 line_info_t *line = &lines[i];
00640 if (line->fd) {
00641 munmap(line->mapped, line->mapped_size);
00642 close(line->fd);
00643 }
00644 }
00645 free(lines);
00646 }
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685 #include <stdarg.h>
00686 #define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
00687 extern int rb_toupper(int c);
00688 #define toupper(c) rb_toupper(c)
00689 #define hex2ascii(hex) (hex2ascii_data[hex])
00690 char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
00691 static inline int imax(int a, int b) { return (a > b ? a : b); }
00692 static int kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap);
00693
00694 static void putce(int c)
00695 {
00696 char s[1];
00697 ssize_t ret;
00698
00699 s[0] = (char)c;
00700 ret = write(2, s, 1);
00701 (void)ret;
00702 }
00703
00704 int
00705 kprintf(const char *fmt, ...)
00706 {
00707 va_list ap;
00708 int retval;
00709
00710 va_start(ap, fmt);
00711 retval = kvprintf(fmt, putce, NULL, 10, ap);
00712 va_end(ap);
00713 return retval;
00714 }
00715
00716
00717
00718
00719
00720
00721
00722 static char *
00723 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
00724 {
00725 char *p, c;
00726
00727 p = nbuf;
00728 *p = '\0';
00729 do {
00730 c = hex2ascii(num % base);
00731 *++p = upper ? toupper(c) : c;
00732 } while (num /= base);
00733 if (lenp)
00734 *lenp = p - nbuf;
00735 return (p);
00736 }
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764 static int
00765 kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap)
00766 {
00767 #define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; }
00768 char nbuf[MAXNBUF];
00769 char *d;
00770 const char *p, *percent, *q;
00771 unsigned char *up;
00772 int ch, n;
00773 uintmax_t num;
00774 int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
00775 int cflag, hflag, jflag, tflag, zflag;
00776 int dwidth, upper;
00777 char padc;
00778 int stop = 0, retval = 0;
00779
00780 num = 0;
00781 if (!func)
00782 d = (char *) arg;
00783 else
00784 d = NULL;
00785
00786 if (fmt == NULL)
00787 fmt = "(fmt null)\n";
00788
00789 if (radix < 2 || radix > 36)
00790 radix = 10;
00791
00792 for (;;) {
00793 padc = ' ';
00794 width = 0;
00795 while ((ch = (unsigned char)*fmt++) != '%' || stop) {
00796 if (ch == '\0')
00797 return (retval);
00798 PCHAR(ch);
00799 }
00800 percent = fmt - 1;
00801 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
00802 sign = 0; dot = 0; dwidth = 0; upper = 0;
00803 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
00804 reswitch: switch (ch = (unsigned char)*fmt++) {
00805 case '.':
00806 dot = 1;
00807 goto reswitch;
00808 case '#':
00809 sharpflag = 1;
00810 goto reswitch;
00811 case '+':
00812 sign = 1;
00813 goto reswitch;
00814 case '-':
00815 ladjust = 1;
00816 goto reswitch;
00817 case '%':
00818 PCHAR(ch);
00819 break;
00820 case '*':
00821 if (!dot) {
00822 width = va_arg(ap, int);
00823 if (width < 0) {
00824 ladjust = !ladjust;
00825 width = -width;
00826 }
00827 } else {
00828 dwidth = va_arg(ap, int);
00829 }
00830 goto reswitch;
00831 case '0':
00832 if (!dot) {
00833 padc = '0';
00834 goto reswitch;
00835 }
00836 case '1': case '2': case '3': case '4':
00837 case '5': case '6': case '7': case '8': case '9':
00838 for (n = 0;; ++fmt) {
00839 n = n * 10 + ch - '0';
00840 ch = *fmt;
00841 if (ch < '0' || ch > '9')
00842 break;
00843 }
00844 if (dot)
00845 dwidth = n;
00846 else
00847 width = n;
00848 goto reswitch;
00849 case 'b':
00850 num = (unsigned int)va_arg(ap, int);
00851 p = va_arg(ap, char *);
00852 for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
00853 PCHAR(*q--);
00854
00855 if (num == 0)
00856 break;
00857
00858 for (tmp = 0; *p;) {
00859 n = *p++;
00860 if (num & (1 << (n - 1))) {
00861 PCHAR(tmp ? ',' : '<');
00862 for (; (n = *p) > ' '; ++p)
00863 PCHAR(n);
00864 tmp = 1;
00865 } else
00866 for (; *p > ' '; ++p)
00867 continue;
00868 }
00869 if (tmp)
00870 PCHAR('>');
00871 break;
00872 case 'c':
00873 PCHAR(va_arg(ap, int));
00874 break;
00875 case 'D':
00876 up = va_arg(ap, unsigned char *);
00877 p = va_arg(ap, char *);
00878 if (!width)
00879 width = 16;
00880 while(width--) {
00881 PCHAR(hex2ascii(*up >> 4));
00882 PCHAR(hex2ascii(*up & 0x0f));
00883 up++;
00884 if (width)
00885 for (q=p;*q;q++)
00886 PCHAR(*q);
00887 }
00888 break;
00889 case 'd':
00890 case 'i':
00891 base = 10;
00892 sign = 1;
00893 goto handle_sign;
00894 case 'h':
00895 if (hflag) {
00896 hflag = 0;
00897 cflag = 1;
00898 } else
00899 hflag = 1;
00900 goto reswitch;
00901 case 'j':
00902 jflag = 1;
00903 goto reswitch;
00904 case 'l':
00905 if (lflag) {
00906 lflag = 0;
00907 qflag = 1;
00908 } else
00909 lflag = 1;
00910 goto reswitch;
00911 case 'n':
00912 if (jflag)
00913 *(va_arg(ap, intmax_t *)) = retval;
00914 else if (qflag)
00915 *(va_arg(ap, int64_t *)) = retval;
00916 else if (lflag)
00917 *(va_arg(ap, long *)) = retval;
00918 else if (zflag)
00919 *(va_arg(ap, size_t *)) = retval;
00920 else if (hflag)
00921 *(va_arg(ap, short *)) = retval;
00922 else if (cflag)
00923 *(va_arg(ap, char *)) = retval;
00924 else
00925 *(va_arg(ap, int *)) = retval;
00926 break;
00927 case 'o':
00928 base = 8;
00929 goto handle_nosign;
00930 case 'p':
00931 base = 16;
00932 sharpflag = (width == 0);
00933 sign = 0;
00934 num = (uintptr_t)va_arg(ap, void *);
00935 goto number;
00936 case 'q':
00937 qflag = 1;
00938 goto reswitch;
00939 case 'r':
00940 base = radix;
00941 if (sign)
00942 goto handle_sign;
00943 goto handle_nosign;
00944 case 's':
00945 p = va_arg(ap, char *);
00946 if (p == NULL)
00947 p = "(null)";
00948 if (!dot)
00949 n = strlen (p);
00950 else
00951 for (n = 0; n < dwidth && p[n]; n++)
00952 continue;
00953
00954 width -= n;
00955
00956 if (!ladjust && width > 0)
00957 while (width--)
00958 PCHAR(padc);
00959 while (n--)
00960 PCHAR(*p++);
00961 if (ladjust && width > 0)
00962 while (width--)
00963 PCHAR(padc);
00964 break;
00965 case 't':
00966 tflag = 1;
00967 goto reswitch;
00968 case 'u':
00969 base = 10;
00970 goto handle_nosign;
00971 case 'X':
00972 upper = 1;
00973 case 'x':
00974 base = 16;
00975 goto handle_nosign;
00976 case 'y':
00977 base = 16;
00978 sign = 1;
00979 goto handle_sign;
00980 case 'z':
00981 zflag = 1;
00982 goto reswitch;
00983 handle_nosign:
00984 sign = 0;
00985 if (jflag)
00986 num = va_arg(ap, uintmax_t);
00987 else if (qflag)
00988 num = va_arg(ap, uint64_t);
00989 else if (tflag)
00990 num = va_arg(ap, ptrdiff_t);
00991 else if (lflag)
00992 num = va_arg(ap, unsigned long);
00993 else if (zflag)
00994 num = va_arg(ap, size_t);
00995 else if (hflag)
00996 num = (unsigned short)va_arg(ap, int);
00997 else if (cflag)
00998 num = (unsigned char)va_arg(ap, int);
00999 else
01000 num = va_arg(ap, unsigned int);
01001 goto number;
01002 handle_sign:
01003 if (jflag)
01004 num = va_arg(ap, intmax_t);
01005 else if (qflag)
01006 num = va_arg(ap, int64_t);
01007 else if (tflag)
01008 num = va_arg(ap, ptrdiff_t);
01009 else if (lflag)
01010 num = va_arg(ap, long);
01011 else if (zflag)
01012 num = va_arg(ap, ssize_t);
01013 else if (hflag)
01014 num = (short)va_arg(ap, int);
01015 else if (cflag)
01016 num = (char)va_arg(ap, int);
01017 else
01018 num = va_arg(ap, int);
01019 number:
01020 if (sign && (intmax_t)num < 0) {
01021 neg = 1;
01022 num = -(intmax_t)num;
01023 }
01024 p = ksprintn(nbuf, num, base, &n, upper);
01025 tmp = 0;
01026 if (sharpflag && num != 0) {
01027 if (base == 8)
01028 tmp++;
01029 else if (base == 16)
01030 tmp += 2;
01031 }
01032 if (neg)
01033 tmp++;
01034
01035 if (!ladjust && padc == '0')
01036 dwidth = width - tmp;
01037 width -= tmp + imax(dwidth, n);
01038 dwidth -= n;
01039 if (!ladjust)
01040 while (width-- > 0)
01041 PCHAR(' ');
01042 if (neg)
01043 PCHAR('-');
01044 if (sharpflag && num != 0) {
01045 if (base == 8) {
01046 PCHAR('0');
01047 } else if (base == 16) {
01048 PCHAR('0');
01049 PCHAR('x');
01050 }
01051 }
01052 while (dwidth-- > 0)
01053 PCHAR('0');
01054
01055 while (*p)
01056 PCHAR(*p--);
01057
01058 if (ladjust)
01059 while (width-- > 0)
01060 PCHAR(' ');
01061
01062 break;
01063 default:
01064 while (percent < fmt)
01065 PCHAR(*percent++);
01066
01067
01068
01069
01070
01071
01072 stop = 1;
01073 break;
01074 }
01075 }
01076 #undef PCHAR
01077 }
01078 #else
01079 #error not supported
01080 #endif
01081