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