1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2005-2020. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "erl_printf_term.h"
26 #include "sys.h"
27 #include "big.h"
28 #include "erl_map.h"
29 #include "erl_binary.h"
30
31 #define PRINT_CHAR(CNT, FN, ARG, C) \
32 do { \
33 int res__ = erts_printf_char((FN), (ARG), (C)); \
34 if (res__ < 0) \
35 return res__; \
36 (CNT) += res__; \
37 } while (0)
38
39 #define PRINT_STRING(CNT, FN, ARG, STR) \
40 do { \
41 int res__ = erts_printf_string((FN), (ARG), (STR)); \
42 if (res__ < 0) \
43 return res__; \
44 (CNT) += res__; \
45 } while (0)
46
47 #define PRINT_BUF(CNT, FN, ARG, BUF, LEN) \
48 do { \
49 int res__ = erts_printf_buf((FN), (ARG), (char*)(BUF), (LEN)); \
50 if (res__ < 0) \
51 return res__; \
52 (CNT) += res__; \
53 } while (0)
54
55 #define PRINT_POINTER(CNT, FN, ARG, PTR) \
56 do { \
57 int res__ = erts_printf_pointer((FN), (ARG), (void *) (PTR)); \
58 if (res__ < 0) \
59 return res__; \
60 (CNT) += res__; \
61 } while (0)
62
63 #define PRINT_UWORD(CNT, FN, ARG, C, P, W, I) \
64 do { \
65 int res__ = erts_printf_uword((FN), (ARG), (C), (P), (W), (I)); \
66 if (res__ < 0) \
67 return res__; \
68 (CNT) += res__; \
69 } while (0)
70
71 #define PRINT_SWORD(CNT, FN, ARG, C, P, W, I) \
72 do { \
73 int res__ = erts_printf_sword((FN), (ARG), (C), (P), (W), (I)); \
74 if (res__ < 0) \
75 return res__; \
76 (CNT) += res__; \
77 } while (0)
78
79 #define PRINT_UWORD64(CNT, FN, ARG, C, P, W, I) \
80 do { \
81 int res__ = erts_printf_uword64((FN), (ARG), (C), (P), (W), (I)); \
82 if (res__ < 0) \
83 return res__; \
84 (CNT) += res__; \
85 } while (0)
86
87 #define PRINT_SWORD64(CNT, FN, ARG, C, P, W, I) \
88 do { \
89 int res__ = erts_printf_sword64((FN), (ARG), (C), (P), (W), (I)); \
90 if (res__ < 0) \
91 return res__; \
92 (CNT) += res__; \
93 } while (0)
94
95 #define PRINT_DOUBLE(CNT, FN, ARG, C, P, W, I) \
96 do { \
97 int res__ = erts_printf_double((FN), (ARG), (C), (P), (W), (I)); \
98 if (res__ < 0) \
99 return res__; \
100 (CNT) += res__; \
101 } while (0)
102
103 #define PRINT_ATOM(CNT, FN, ARG, ATOM, DCOUNT) \
104 do { \
105 int res__ = print_atom_name(FN, ARG, ATOM, DCOUNT); \
106 if (res__ < 0) \
107 return res__; \
108 (CNT) += res__; \
109 if (*(DCOUNT) <= 0) \
110 goto L_done; \
111 } while(0)
112
113
114 /* CTYPE macros */
115
116 #define LATIN1
117
118 #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
119 #ifdef LATIN1
120 #define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z') \
121 || ((c) >= 128+95 && (c) <= 255 && (c) != 247))
122 #define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z') \
123 || ((c) >= 128+64 && (c) <= 128+94 && (c) != 247-32))
124 #else
125 #define IS_LOWER(c) ((c) >= 'a' && (c) <= 'z')
126 #define IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
127 #endif
128
129 #define IS_ALNUM(c) (IS_DIGIT(c) || IS_LOWER(c) || IS_UPPER(c))
130
131 /* We don't include 160 (non-breaking space). */
132 #define IS_SPACE(c) (c == ' ' || c == '\n' || c == '\t' || c == '\r')
133
134 #ifdef LATIN1
135 #define IS_CNTRL(c) ((c) < ' ' || (c) == 127 \
136 || ((c) >= 128 && (c) < 128+32))
137 #else
138 /* Treat all non-ASCII as control characters */
139 #define IS_CNTRL(c) ((c) < ' ' || (c) >= 127)
140 #endif
141
142 /* return 0 if list is not a non-empty flat list of printable characters */
143
144 static int
is_printable_string(Eterm list)145 is_printable_string(Eterm list) {
146 int len = 0;
147 int c;
148
149 while(is_list(list)) {
150 Eterm* consp = list_val(list);
151 Eterm hd = CAR(consp);
152
153 if (!is_byte(hd))
154 return 0;
155 c = signed_val(hd);
156 /* IS_PRINT || IS_SPACE would be another way to put it */
157 if (IS_CNTRL(c) && !IS_SPACE(c))
158 return 0;
159 len++;
160 list = CDR(consp);
161 }
162 if (is_nil(list))
163 return len;
164 return 0;
165 }
166
is_printable_ascii(byte * bytep,Uint bytesize,Uint bitoffs)167 static int is_printable_ascii(byte* bytep, Uint bytesize, Uint bitoffs)
168 {
169 if (!bitoffs) {
170 while (bytesize--) {
171 if (*bytep < ' ' || *bytep >= 127)
172 return 0;
173 bytep++;
174 }
175 } else {
176 while (bytesize--) {
177 byte octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs));
178 if (octet < ' ' || octet >= 127)
179 return 0;
180 bytep++;
181 }
182 }
183 return 1;
184 }
185
186 /*
187 * Helper function for print_atom_name(). Not generally useful.
188 */
latin1_char(int c1,int c2)189 static ERTS_INLINE int latin1_char(int c1, int c2)
190 {
191 if ((c1 & 0x80) == 0) {
192 /* Plain old 7-bit ASCII. */
193 return c1;
194 } else if ((c1 & 0xE0) == 0xC0) {
195 /* Unicode code points from 0x80 through 0x7FF. */
196 ASSERT((c2 & 0xC0) == 0x80);
197 return (c1 & 0x1F) << 6 | (c2 & 0x3F);
198 } else if ((c1 & 0xC0) == 0x80) {
199 /* A continutation byte in a utf8 sequence. Pretend that it is
200 * a character that is allowed in an atom. */
201 return 'a';
202 } else {
203 /* The start of a utf8 sequence comprising three or four
204 * bytes. Always needs quoting. */
205 return 0;
206 }
207 }
208
209 /*
210 * Print a atom, quoting it if necessary.
211 *
212 * Atoms are encoded in utf8. Since we have full control over creation
213 * of atoms, the utf8 encoding is always correct and there is no need
214 * to check for errors.
215 */
print_atom_name(fmtfn_t fn,void * arg,Eterm atom,long * dcount)216 static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount)
217 {
218 int n, i;
219 int res;
220 int need_quote;
221 int pos;
222 byte *s;
223 byte *cpos;
224 int c;
225 int lc;
226
227 res = 0;
228 i = atom_val(atom);
229
230 if ((i < 0) || (i >= atom_table_size()) || (atom_tab(i) == NULL)) {
231 PRINT_STRING(res, fn, arg, "<bad atom index: ");
232 PRINT_SWORD(res, fn, arg, 'd', 0, 1, (ErlPfSWord) i);
233 PRINT_CHAR(res, fn, arg, '>');
234 return res;
235 }
236
237 s = atom_tab(i)->name;
238 n = atom_tab(i)->len;
239
240 *dcount -= atom_tab(i)->len;
241
242 if (n == 0) {
243 /* The empty atom: '' */
244 PRINT_STRING(res, fn, arg, "''");
245 return res;
246 }
247
248 /*
249 * Find out whether the atom will need quoting. Quoting is not necessary
250 * if the following applies:
251 *
252 * - The first character is a lowercase letter in the Latin-1 code
253 * block (0-255).
254 *
255 * - All other characters are either alphanumeric characters in
256 * the Latin-1 code block or the character '_'.
257 */
258
259 need_quote = 0;
260 cpos = s;
261 pos = n - 1;
262 c = *cpos++;
263 lc = latin1_char(c, *cpos);
264 if (!IS_LOWER(lc))
265 need_quote++;
266 else {
267 while (pos--) {
268 c = *cpos++;
269 lc = latin1_char(c, *cpos);
270 if (!IS_ALNUM(lc) && lc != '_') {
271 need_quote++;
272 break;
273 }
274 }
275 }
276
277 /*
278 * Now output the atom, including single quotes if needed.
279 *
280 * Control characters (including the range 128-159) must
281 * be specially printed. Therefore, we must do a partial
282 * decoding of the utf8 encoding.
283 */
284 cpos = s;
285 pos = n;
286 if (need_quote)
287 PRINT_CHAR(res, fn, arg, '\'');
288 while(pos--) {
289 c = *cpos++;
290 switch(c) {
291 case '\'': PRINT_STRING(res, fn, arg, "\\'"); break;
292 case '\\': PRINT_STRING(res, fn, arg, "\\\\"); break;
293 case '\n': PRINT_STRING(res, fn, arg, "\\n"); break;
294 case '\f': PRINT_STRING(res, fn, arg, "\\f"); break;
295 case '\t': PRINT_STRING(res, fn, arg, "\\t"); break;
296 case '\r': PRINT_STRING(res, fn, arg, "\\r"); break;
297 case '\b': PRINT_STRING(res, fn, arg, "\\b"); break;
298 case '\v': PRINT_STRING(res, fn, arg, "\\v"); break;
299 default:
300 if (c < ' ') {
301 /* ASCII control character (0-31). */
302 PRINT_CHAR(res, fn, arg, '\\');
303 PRINT_UWORD(res, fn, arg, 'o', 1, 3, (ErlPfUWord) c);
304 } else if (c >= 0x80) {
305 /* A multi-byte utf8-encoded code point. Determine the
306 * length of the sequence. */
307 int n;
308 if ((c & 0xE0) == 0xC0) {
309 n = 2;
310 } else if ((c & 0xF0) == 0xE0) {
311 n = 3;
312 } else {
313 ASSERT((c & 0xF8) == 0xF0);
314 n = 4;
315 }
316 ASSERT(pos - n + 1 >= 0);
317
318 if (c == 0xC2 && *cpos < 0xA0) {
319 /* Extended ASCII control character (128-159). */
320 ASSERT(pos > 0);
321 ASSERT(0x80 <= *cpos);
322 PRINT_CHAR(res, fn, arg, '\\');
323 PRINT_UWORD(res, fn, arg, 'o', 1, 3, (ErlPfUWord) *cpos);
324 pos--, cpos++;
325 } else {
326 PRINT_BUF(res, fn, arg, cpos-1, n);
327 cpos += n - 1;
328 pos -= n - 1;
329 }
330 } else {
331 /* Printable ASCII character. */
332 PRINT_CHAR(res, fn, arg, (char) c);
333 }
334 break;
335 }
336 }
337 if (need_quote)
338 PRINT_CHAR(res, fn, arg, '\'');
339 return res;
340 }
341
342 #define PRT_BAR ((Eterm) 0)
343 #define PRT_COMMA ((Eterm) 1)
344 #define PRT_CLOSE_LIST ((Eterm) 2)
345 #define PRT_CLOSE_TUPLE ((Eterm) 3)
346 #define PRT_ASSOC ((Eterm) 4)
347 #define PRT_TERM ((Eterm) 5)
348 #define PRT_ONE_CONS ((Eterm) 6)
349 #define PRT_PATCH_FUN_SIZE ((Eterm) 7)
350 #define PRT_LAST_ARRAY_ELEMENT ((Eterm) 8) /* Note! Must be last... */
351
352 #if 0
353 static char *format_binary(Uint16 x, char *b) {
354 int z;
355 b[16] = '\0';
356 for (z = 0; z < 16; z++) {
357 b[15-z] = ((x>>z) & 0x1) ? '1' : '0';
358 }
359 return b;
360 }
361 #endif
362
363 static int
print_term(fmtfn_t fn,void * arg,Eterm obj,long * dcount)364 print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) {
365 DECLARE_WSTACK(s);
366 int res;
367 int i;
368 Eterm val;
369 Uint32 *ref_num;
370 union {
371 UWord word;
372 Eterm* ptr;
373 }popped;
374 Eterm* nobj;
375 Wterm wobj;
376
377 res = 0;
378
379 goto L_jump_start;
380
381 L_outer_loop:
382 while (!WSTACK_ISEMPTY(s)) {
383 switch (val = WSTACK_POP(s)) {
384 case PRT_COMMA:
385 PRINT_CHAR(res, fn, arg, ',');
386 goto L_outer_loop;
387 case PRT_BAR:
388 PRINT_CHAR(res, fn, arg, '|');
389 goto L_outer_loop;
390 case PRT_CLOSE_LIST:
391 PRINT_CHAR(res, fn, arg, ']');
392 goto L_outer_loop;
393 case PRT_CLOSE_TUPLE:
394 PRINT_CHAR(res, fn, arg, '}');
395 goto L_outer_loop;
396 case PRT_ASSOC:
397 PRINT_STRING(res, fn, arg, "=>");
398 goto L_outer_loop;
399 default:
400 popped.word = WSTACK_POP(s);
401
402 switch (val) {
403 case PRT_TERM:
404 obj = (Eterm) popped.word;
405 break;
406 case PRT_ONE_CONS:
407 obj = (Eterm) popped.word;
408 L_print_one_cons:
409 {
410 Eterm* cons = list_val(obj);
411 Eterm tl;
412
413 obj = CAR(cons);
414 tl = CDR(cons);
415 if (is_not_nil(tl)) {
416 if (is_list(tl)) {
417 WSTACK_PUSH3(s, tl, PRT_ONE_CONS, PRT_COMMA);
418 } else {
419 WSTACK_PUSH3(s, tl, PRT_TERM, PRT_BAR);
420 }
421 }
422 }
423 break;
424 case PRT_LAST_ARRAY_ELEMENT:
425 obj = *popped.ptr;
426 break;
427 default: /* PRT_LAST_ARRAY_ELEMENT+1 and upwards */
428 obj = *popped.ptr;
429 WSTACK_PUSH3(s, (UWord) (popped.ptr + 1), val-1, PRT_COMMA);
430 break;
431 }
432 break;
433 }
434
435 L_jump_start:
436
437 if ((*dcount)-- <= 0)
438 goto L_done;
439
440 if (is_non_value(obj)) {
441 PRINT_STRING(res, fn, arg, "<TNV>");
442 goto L_done;
443 } else if (is_CP(obj)) {
444 const ErtsCodeMFA* mfa = erts_find_function_from_pc(cp_val(obj));
445 if (mfa) {
446 const UWord *func_start = erts_codemfa_to_code(mfa);
447 const UWord *cp_addr = (UWord*)cp_val(obj);
448
449 PRINT_STRING(res, fn, arg, "<");
450 PRINT_ATOM(res, fn, arg, mfa->module, dcount);
451 PRINT_STRING(res, fn, arg, ":");
452 PRINT_ATOM(res, fn, arg, mfa->function, dcount);
453 PRINT_STRING(res, fn, arg, "/");
454 PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) mfa->arity);
455 PRINT_STRING(res, fn, arg, "+");
456 PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) (cp_addr - func_start));
457 PRINT_STRING(res, fn, arg, ">");
458 } else {
459 PRINT_STRING(res, fn, arg, "<cp/header:");
460 PRINT_POINTER(res, fn, arg, cp_val(obj));
461 PRINT_CHAR(res, fn, arg, '>');
462 }
463 goto L_done;
464 }
465 wobj = (Wterm)obj;
466 switch (tag_val_def(wobj)) {
467 case NIL_DEF:
468 PRINT_STRING(res, fn, arg, "[]");
469 break;
470 case ATOM_DEF: {
471 PRINT_ATOM(res, fn, arg, obj, dcount);
472 break;
473 }
474 case SMALL_DEF:
475 PRINT_SWORD(res, fn, arg, 'd', 0, 1, (ErlPfSWord) signed_val(obj));
476 break;
477 case BIG_DEF: {
478 int print_res;
479 char def_buf[64];
480 char *buf, *big_str;
481 Uint sz = (Uint) big_integer_estimate(wobj, 10);
482 sz++;
483 if (sz <= 64)
484 buf = &def_buf[0];
485 else
486 buf = erts_alloc(ERTS_ALC_T_TMP, sz);
487 big_str = erts_big_to_string(wobj, 10, buf, sz);
488 print_res = erts_printf_string(fn, arg, big_str);
489 if (buf != &def_buf[0])
490 erts_free(ERTS_ALC_T_TMP, (void *) buf);
491 if (print_res < 0) {
492 res = print_res;
493 goto L_done;
494 }
495 res += print_res;
496 break;
497 }
498 case REF_DEF:
499 if (!ERTS_IS_CRASH_DUMPING)
500 erts_magic_ref_save_bin(obj);
501 /* fall through... */
502 case EXTERNAL_REF_DEF:
503 PRINT_STRING(res, fn, arg, "#Ref<");
504 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
505 (ErlPfUWord) ref_channel_no(wobj));
506 ref_num = ref_numbers(wobj);
507 for (i = ref_no_numbers(wobj)-1; i >= 0; i--) {
508 PRINT_CHAR(res, fn, arg, '.');
509 PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) ref_num[i]);
510 }
511 PRINT_CHAR(res, fn, arg, '>');
512 break;
513 case PID_DEF:
514 case EXTERNAL_PID_DEF:
515 PRINT_CHAR(res, fn, arg, '<');
516 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
517 (ErlPfUWord) pid_channel_no(wobj));
518 PRINT_CHAR(res, fn, arg, '.');
519 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
520 (ErlPfUWord) pid_number(wobj));
521 PRINT_CHAR(res, fn, arg, '.');
522 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
523 (ErlPfUWord) pid_serial(wobj));
524 PRINT_CHAR(res, fn, arg, '>');
525 break;
526 case PORT_DEF:
527 case EXTERNAL_PORT_DEF:
528 PRINT_STRING(res, fn, arg, "#Port<");
529 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
530 (ErlPfUWord) port_channel_no(wobj));
531 PRINT_CHAR(res, fn, arg, '.');
532 PRINT_UWORD64(res, fn, arg, 'u', 0, 1,
533 (ErlPfUWord64) port_number(wobj));
534 PRINT_CHAR(res, fn, arg, '>');
535 break;
536 case LIST_DEF:
537 if (is_printable_string(obj)) {
538 int c;
539 PRINT_CHAR(res, fn, arg, '"');
540 nobj = list_val(obj);
541 while (1) {
542 if ((*dcount)-- <= 0)
543 goto L_done;
544 c = signed_val(*nobj++);
545 if (c == '\n')
546 PRINT_STRING(res, fn, arg, "\\n");
547 else {
548 if (c == '"')
549 PRINT_CHAR(res, fn, arg, '\\');
550 PRINT_CHAR(res, fn, arg, (char) c);
551 }
552 if (is_not_list(*nobj))
553 break;
554 nobj = list_val(*nobj);
555 }
556 PRINT_CHAR(res, fn, arg, '"');
557 } else {
558 PRINT_CHAR(res, fn, arg, '[');
559 WSTACK_PUSH(s,PRT_CLOSE_LIST);
560 goto L_print_one_cons;
561 }
562 break;
563 case TUPLE_DEF:
564 nobj = tuple_val(wobj); /* pointer to arity */
565 i = arityval(*nobj); /* arity */
566 PRINT_CHAR(res, fn, arg, '{');
567 WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
568 ++nobj;
569 if (i > 0) {
570 WSTACK_PUSH2(s, (UWord) nobj, PRT_LAST_ARRAY_ELEMENT+i-1);
571 }
572 break;
573 case FLOAT_DEF: {
574 FloatDef ff;
575 GET_DOUBLE(wobj, ff);
576 PRINT_DOUBLE(res, fn, arg, 'e', 6, 0, ff.fd);
577 }
578 break;
579 case BINARY_DEF:
580 {
581 byte* bytep;
582 Uint bytesize = binary_size(obj);
583 Uint bitoffs;
584 Uint bitsize;
585 byte octet;
586 ERTS_GET_BINARY_BYTES(obj, bytep, bitoffs, bitsize);
587
588 if (bitsize || !bytesize
589 || !is_printable_ascii(bytep, bytesize, bitoffs)) {
590 int is_first = 1;
591 PRINT_STRING(res, fn, arg, "<<");
592 while (bytesize) {
593 if (is_first)
594 is_first = 0;
595 else
596 PRINT_CHAR(res, fn, arg, ',');
597 if (bitoffs)
598 octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs));
599 else
600 octet = bytep[0];
601 PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet);
602 ++bytep;
603 --bytesize;
604 if ((*dcount)-- <= 0)
605 goto L_done;
606 }
607 if (bitsize) {
608 Uint bits = bitoffs + bitsize;
609 octet = bytep[0];
610 if (bits < 8)
611 octet >>= 8 - bits;
612 else if (bits > 8) {
613 bits -= 8; /* bits in last byte */
614 octet <<= bits;
615 octet |= bytep[1] >> (8 - bits);
616 }
617 octet &= (1 << bitsize) - 1;
618 if (is_first)
619 is_first = 0;
620 else
621 PRINT_CHAR(res, fn, arg, ',');
622 PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet);
623 PRINT_CHAR(res, fn, arg, ':');
624 PRINT_UWORD(res, fn, arg, 'u', 0, 1, bitsize);
625 }
626 PRINT_STRING(res, fn, arg, ">>");
627 }
628 else {
629 PRINT_STRING(res, fn, arg, "<<\"");
630 while (bytesize) {
631 if (bitoffs)
632 octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs));
633 else
634 octet = bytep[0];
635 if (octet == '"')
636 PRINT_CHAR(res, fn, arg, '\\');
637 PRINT_CHAR(res, fn, arg, octet);
638 ++bytep;
639 --bytesize;
640 if ((*dcount)-- <= 0)
641 goto L_done;
642 }
643 PRINT_STRING(res, fn, arg, "\">>");
644 }
645 }
646 break;
647 case EXPORT_DEF:
648 {
649 Export* ep = *((Export **) (export_val(wobj) + 1));
650 long tdcount;
651 int tres;
652
653 PRINT_STRING(res, fn, arg, "fun ");
654
655 /* We pass a temporary 'dcount' and adjust the real one later to ensure
656 * that the fun doesn't get split up between the module and function
657 * name. */
658 tdcount = MAX_ATOM_SZ_LIMIT;
659 tres = print_atom_name(fn, arg, ep->info.mfa.module, &tdcount);
660 if (tres < 0) {
661 res = tres;
662 goto L_done;
663 }
664 *dcount -= (MAX_ATOM_SZ_LIMIT - tdcount);
665 res += tres;
666
667 PRINT_CHAR(res, fn, arg, ':');
668
669 tdcount = MAX_ATOM_SZ_LIMIT;
670 tres = print_atom_name(fn, arg, ep->info.mfa.function, &tdcount);
671 if (tres < 0) {
672 res = tres;
673 goto L_done;
674 }
675 *dcount -= (MAX_ATOM_SZ_LIMIT - tdcount);
676 res += tres;
677
678 PRINT_CHAR(res, fn, arg, '/');
679 PRINT_SWORD(res, fn, arg, 'd', 0, 1,
680 (ErlPfSWord) ep->info.mfa.arity);
681 }
682 break;
683 case FUN_DEF:
684 {
685 ErlFunThing *funp = (ErlFunThing *) fun_val(wobj);
686 Atom *ap = atom_tab(atom_val(funp->fe->module));
687
688 PRINT_STRING(res, fn, arg, "#Fun<");
689 PRINT_BUF(res, fn, arg, ap->name, ap->len);
690 PRINT_CHAR(res, fn, arg, '.');
691 PRINT_SWORD(res, fn, arg, 'd', 0, 1,
692 (ErlPfSWord) funp->fe->old_index);
693 PRINT_CHAR(res, fn, arg, '.');
694 PRINT_SWORD(res, fn, arg, 'd', 0, 1,
695 (ErlPfSWord) funp->fe->old_uniq);
696 PRINT_CHAR(res, fn, arg, '>');
697 }
698 break;
699 case MAP_DEF: {
700 Eterm *head = boxed_val(wobj);
701
702 if (is_flatmap_header(*head)) {
703 Uint n;
704 Eterm *ks, *vs;
705 n = flatmap_get_size(head);
706 ks = flatmap_get_keys(head);
707 vs = flatmap_get_values(head);
708
709 PRINT_CHAR(res, fn, arg, '#');
710 PRINT_CHAR(res, fn, arg, '{');
711 WSTACK_PUSH(s, PRT_CLOSE_TUPLE);
712 if (n > 0) {
713 n--;
714 WSTACK_PUSH5(s, vs[n], PRT_TERM, PRT_ASSOC, ks[n], PRT_TERM);
715 while (n--) {
716 WSTACK_PUSH6(s, PRT_COMMA, vs[n], PRT_TERM, PRT_ASSOC,
717 ks[n], PRT_TERM);
718 }
719 }
720 } else {
721 Uint n, mapval;
722 mapval = MAP_HEADER_VAL(*head);
723 switch (MAP_HEADER_TYPE(*head)) {
724 case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
725 case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
726 PRINT_STRING(res, fn, arg, "#<");
727 PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval);
728 PRINT_STRING(res, fn, arg, ">{");
729 WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
730 n = hashmap_bitcount(mapval);
731 ASSERT(n < 17);
732 head += 2;
733 if (n > 0) {
734 n--;
735 WSTACK_PUSH(s, head[n]);
736 WSTACK_PUSH(s, PRT_TERM);
737 while (n--) {
738 WSTACK_PUSH(s, PRT_COMMA);
739 WSTACK_PUSH(s, head[n]);
740 WSTACK_PUSH(s, PRT_TERM);
741 }
742 }
743 break;
744 case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
745 n = hashmap_bitcount(mapval);
746 head++;
747 PRINT_CHAR(res, fn, arg, '<');
748 PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval);
749 PRINT_STRING(res, fn, arg, ">{");
750 WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
751 ASSERT(n < 17);
752 if (n > 0) {
753 n--;
754 WSTACK_PUSH(s, head[n]);
755 WSTACK_PUSH(s, PRT_TERM);
756 while (n--) {
757 WSTACK_PUSH(s, PRT_COMMA);
758 WSTACK_PUSH(s, head[n]);
759 WSTACK_PUSH(s, PRT_TERM);
760 }
761 }
762 break;
763 }
764 }
765 break;
766 }
767 case MATCHSTATE_DEF:
768 PRINT_STRING(res, fn, arg, "#MatchState");
769 break;
770 default:
771 PRINT_STRING(res, fn, arg, "<unknown:");
772 PRINT_POINTER(res, fn, arg, wobj);
773 PRINT_CHAR(res, fn, arg, '>');
774 break;
775 }
776 }
777
778 L_done:
779 DESTROY_WSTACK(s);
780 return res;
781 }
782
783
784 int
erts_printf_term(fmtfn_t fn,void * arg,ErlPfEterm term,long precision)785 erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision) {
786 int res;
787 ERTS_CT_ASSERT(sizeof(ErlPfEterm) == sizeof(Eterm));
788
789 res = print_term(fn, arg, (Eterm)term, &precision);
790 if (res < 0)
791 return res;
792 if (precision <= 0)
793 PRINT_STRING(res, fn, arg, "... ");
794 return res;
795 }
796