1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2005-2018. 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_DOUBLE(CNT, FN, ARG, C, P, W, I) \
80 do { \
81 int res__ = erts_printf_double((FN), (ARG), (C), (P), (W), (I)); \
82 if (res__ < 0) \
83 return res__; \
84 (CNT) += res__; \
85 } while (0)
86
87 /* CTYPE macros */
88
89 #define LATIN1
90
91 #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
92 #ifdef LATIN1
93 #define IS_LOWER(c) (((c) >= 'a' && (c) <= 'z') \
94 || ((c) >= 128+95 && (c) <= 255 && (c) != 247))
95 #define IS_UPPER(c) (((c) >= 'A' && (c) <= 'Z') \
96 || ((c) >= 128+64 && (c) <= 128+94 && (c) != 247-32))
97 #else
98 #define IS_LOWER(c) ((c) >= 'a' && (c) <= 'z')
99 #define IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
100 #endif
101
102 #define IS_ALNUM(c) (IS_DIGIT(c) || IS_LOWER(c) || IS_UPPER(c))
103
104 /* We don't include 160 (non-breaking space). */
105 #define IS_SPACE(c) (c == ' ' || c == '\n' || c == '\t' || c == '\r')
106
107 #ifdef LATIN1
108 #define IS_CNTRL(c) ((c) < ' ' || (c) == 127 \
109 || ((c) >= 128 && (c) < 128+32))
110 #else
111 /* Treat all non-ASCII as control characters */
112 #define IS_CNTRL(c) ((c) < ' ' || (c) >= 127)
113 #endif
114
115 #define IS_PRINT(c) (!IS_CNTRL(c))
116
117 /* return 0 if list is not a non-empty flat list of printable characters */
118
119 static int
is_printable_string(Eterm list)120 is_printable_string(Eterm list) {
121 int len = 0;
122 int c;
123
124 while(is_list(list)) {
125 Eterm* consp = list_val(list);
126 Eterm hd = CAR(consp);
127
128 if (!is_byte(hd))
129 return 0;
130 c = signed_val(hd);
131 /* IS_PRINT || IS_SPACE would be another way to put it */
132 if (IS_CNTRL(c) && !IS_SPACE(c))
133 return 0;
134 len++;
135 list = CDR(consp);
136 }
137 if (is_nil(list))
138 return len;
139 return 0;
140 }
141
is_printable_ascii(byte * bytep,Uint bytesize,Uint bitoffs)142 static int is_printable_ascii(byte* bytep, Uint bytesize, Uint bitoffs)
143 {
144 if (!bitoffs) {
145 while (bytesize--) {
146 if (*bytep < ' ' || *bytep >= 127)
147 return 0;
148 bytep++;
149 }
150 } else {
151 while (bytesize--) {
152 byte octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs));
153 if (octet < ' ' || octet >= 127)
154 return 0;
155 bytep++;
156 }
157 }
158 return 1;
159 }
160
161 /* print a atom doing what quoting is necessary */
print_atom_name(fmtfn_t fn,void * arg,Eterm atom,long * dcount)162 static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount)
163 {
164 int n, i;
165 int res;
166 int need_quote;
167 int pos;
168 byte *s;
169 byte *cpos;
170 int c;
171
172 res = 0;
173 i = atom_val(atom);
174
175 if ((i < 0) || (i >= atom_table_size()) || (atom_tab(i) == NULL)) {
176 PRINT_STRING(res, fn, arg, "<bad atom index: ");
177 PRINT_SWORD(res, fn, arg, 'd', 0, 1, (ErlPfSWord) i);
178 PRINT_CHAR(res, fn, arg, '>');
179 return res;
180 }
181
182 s = atom_tab(i)->name;
183 n = atom_tab(i)->len;
184
185 *dcount -= atom_tab(i)->len;
186
187 if (n == 0) {
188 PRINT_STRING(res, fn, arg, "''");
189 return res;
190 }
191
192
193 need_quote = 0;
194 cpos = s;
195 pos = n - 1;
196
197 c = *cpos++;
198 if (!IS_LOWER(c))
199 need_quote++;
200 else {
201 while (pos--) {
202 c = *cpos++;
203 if (!IS_ALNUM(c) && (c != '_')) {
204 need_quote++;
205 break;
206 }
207 }
208 }
209 cpos = s;
210 pos = n;
211 if (need_quote)
212 PRINT_CHAR(res, fn, arg, '\'');
213 while(pos--) {
214 c = *cpos++;
215 switch(c) {
216 case '\'': PRINT_STRING(res, fn, arg, "\\'"); break;
217 case '\\': PRINT_STRING(res, fn, arg, "\\\\"); break;
218 case '\n': PRINT_STRING(res, fn, arg, "\\n"); break;
219 case '\f': PRINT_STRING(res, fn, arg, "\\f"); break;
220 case '\t': PRINT_STRING(res, fn, arg, "\\t"); break;
221 case '\r': PRINT_STRING(res, fn, arg, "\\r"); break;
222 case '\b': PRINT_STRING(res, fn, arg, "\\b"); break;
223 case '\v': PRINT_STRING(res, fn, arg, "\\v"); break;
224 default:
225 if (IS_CNTRL(c)) {
226 PRINT_CHAR(res, fn, arg, '\\');
227 PRINT_UWORD(res, fn, arg, 'o', 1, 3, (ErlPfUWord) c);
228 }
229 else
230 PRINT_CHAR(res, fn, arg, (char) c);
231 break;
232 }
233 }
234 if (need_quote)
235 PRINT_CHAR(res, fn, arg, '\'');
236 return res;
237 }
238
239
240 #define PRT_BAR ((Eterm) 0)
241 #define PRT_COMMA ((Eterm) 1)
242 #define PRT_CLOSE_LIST ((Eterm) 2)
243 #define PRT_CLOSE_TUPLE ((Eterm) 3)
244 #define PRT_ASSOC ((Eterm) 4)
245 #define PRT_TERM ((Eterm) 5)
246 #define PRT_ONE_CONS ((Eterm) 6)
247 #define PRT_PATCH_FUN_SIZE ((Eterm) 7)
248 #define PRT_LAST_ARRAY_ELEMENT ((Eterm) 8) /* Note! Must be last... */
249
250 #if 0
251 static char *format_binary(Uint16 x, char *b) {
252 int z;
253 b[16] = '\0';
254 for (z = 0; z < 16; z++) {
255 b[15-z] = ((x>>z) & 0x1) ? '1' : '0';
256 }
257 return b;
258 }
259 #endif
260
261 static int
print_term(fmtfn_t fn,void * arg,Eterm obj,long * dcount)262 print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) {
263 DECLARE_WSTACK(s);
264 int res;
265 int i;
266 Eterm val;
267 Uint32 *ref_num;
268 union {
269 UWord word;
270 Eterm* ptr;
271 }popped;
272 Eterm* nobj;
273 Wterm wobj;
274
275 res = 0;
276
277 goto L_jump_start;
278
279 L_outer_loop:
280 while (!WSTACK_ISEMPTY(s)) {
281 switch (val = WSTACK_POP(s)) {
282 case PRT_COMMA:
283 PRINT_CHAR(res, fn, arg, ',');
284 goto L_outer_loop;
285 case PRT_BAR:
286 PRINT_CHAR(res, fn, arg, '|');
287 goto L_outer_loop;
288 case PRT_CLOSE_LIST:
289 PRINT_CHAR(res, fn, arg, ']');
290 goto L_outer_loop;
291 case PRT_CLOSE_TUPLE:
292 PRINT_CHAR(res, fn, arg, '}');
293 goto L_outer_loop;
294 case PRT_ASSOC:
295 PRINT_STRING(res, fn, arg, "=>");
296 goto L_outer_loop;
297 default:
298 popped.word = WSTACK_POP(s);
299
300 switch (val) {
301 case PRT_TERM:
302 obj = (Eterm) popped.word;
303 break;
304 case PRT_ONE_CONS:
305 obj = (Eterm) popped.word;
306 L_print_one_cons:
307 {
308 Eterm* cons = list_val(obj);
309 Eterm tl;
310
311 obj = CAR(cons);
312 tl = CDR(cons);
313 if (is_not_nil(tl)) {
314 if (is_list(tl)) {
315 WSTACK_PUSH3(s, tl, PRT_ONE_CONS, PRT_COMMA);
316 } else {
317 WSTACK_PUSH3(s, tl, PRT_TERM, PRT_BAR);
318 }
319 }
320 }
321 break;
322 case PRT_LAST_ARRAY_ELEMENT:
323 obj = *popped.ptr;
324 break;
325 default: /* PRT_LAST_ARRAY_ELEMENT+1 and upwards */
326 obj = *popped.ptr;
327 WSTACK_PUSH3(s, (UWord) (popped.ptr + 1), val-1, PRT_COMMA);
328 break;
329 }
330 break;
331 }
332
333 L_jump_start:
334
335 if ((*dcount)-- <= 0)
336 goto L_done;
337
338 if (is_CP(obj)) {
339 PRINT_STRING(res, fn, arg, "<cp/header:");
340 PRINT_POINTER(res, fn, arg, cp_val(obj));
341 PRINT_CHAR(res, fn, arg, '>');
342 goto L_done;
343 }
344 wobj = (Wterm)obj;
345 switch (tag_val_def(wobj)) {
346 case NIL_DEF:
347 PRINT_STRING(res, fn, arg, "[]");
348 break;
349 case ATOM_DEF: {
350 int tres = print_atom_name(fn, arg, obj, dcount);
351 if (tres < 0) {
352 res = tres;
353 goto L_done;
354 }
355 res += tres;
356 if (*dcount <= 0)
357 goto L_done;
358 break;
359 }
360 case SMALL_DEF:
361 PRINT_SWORD(res, fn, arg, 'd', 0, 1, (ErlPfSWord) signed_val(obj));
362 break;
363 case BIG_DEF: {
364 int print_res;
365 char def_buf[64];
366 char *buf, *big_str;
367 Uint sz = (Uint) big_integer_estimate(wobj, 10);
368 sz++;
369 if (sz <= 64)
370 buf = &def_buf[0];
371 else
372 buf = erts_alloc(ERTS_ALC_T_TMP, sz);
373 big_str = erts_big_to_string(wobj, 10, buf, sz);
374 print_res = erts_printf_string(fn, arg, big_str);
375 if (buf != &def_buf[0])
376 erts_free(ERTS_ALC_T_TMP, (void *) buf);
377 if (print_res < 0) {
378 res = print_res;
379 goto L_done;
380 }
381 res += print_res;
382 break;
383 }
384 case REF_DEF:
385 if (!ERTS_IS_CRASH_DUMPING)
386 erts_magic_ref_save_bin(obj);
387 /* fall through... */
388 case EXTERNAL_REF_DEF:
389 PRINT_STRING(res, fn, arg, "#Ref<");
390 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
391 (ErlPfUWord) ref_channel_no(wobj));
392 ref_num = ref_numbers(wobj);
393 for (i = ref_no_numbers(wobj)-1; i >= 0; i--) {
394 PRINT_CHAR(res, fn, arg, '.');
395 PRINT_UWORD(res, fn, arg, 'u', 0, 1, (ErlPfUWord) ref_num[i]);
396 }
397 PRINT_CHAR(res, fn, arg, '>');
398 break;
399 case PID_DEF:
400 case EXTERNAL_PID_DEF:
401 PRINT_CHAR(res, fn, arg, '<');
402 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
403 (ErlPfUWord) pid_channel_no(wobj));
404 PRINT_CHAR(res, fn, arg, '.');
405 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
406 (ErlPfUWord) pid_number(wobj));
407 PRINT_CHAR(res, fn, arg, '.');
408 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
409 (ErlPfUWord) pid_serial(wobj));
410 PRINT_CHAR(res, fn, arg, '>');
411 break;
412 case PORT_DEF:
413 case EXTERNAL_PORT_DEF:
414 PRINT_STRING(res, fn, arg, "#Port<");
415 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
416 (ErlPfUWord) port_channel_no(wobj));
417 PRINT_CHAR(res, fn, arg, '.');
418 PRINT_UWORD(res, fn, arg, 'u', 0, 1,
419 (ErlPfUWord) port_number(wobj));
420 PRINT_CHAR(res, fn, arg, '>');
421 break;
422 case LIST_DEF:
423 if (is_printable_string(obj)) {
424 int c;
425 PRINT_CHAR(res, fn, arg, '"');
426 nobj = list_val(obj);
427 while (1) {
428 if ((*dcount)-- <= 0)
429 goto L_done;
430 c = signed_val(*nobj++);
431 if (c == '\n')
432 PRINT_STRING(res, fn, arg, "\\n");
433 else {
434 if (c == '"')
435 PRINT_CHAR(res, fn, arg, '\\');
436 PRINT_CHAR(res, fn, arg, (char) c);
437 }
438 if (is_not_list(*nobj))
439 break;
440 nobj = list_val(*nobj);
441 }
442 PRINT_CHAR(res, fn, arg, '"');
443 } else {
444 PRINT_CHAR(res, fn, arg, '[');
445 WSTACK_PUSH(s,PRT_CLOSE_LIST);
446 goto L_print_one_cons;
447 }
448 break;
449 case TUPLE_DEF:
450 nobj = tuple_val(wobj); /* pointer to arity */
451 i = arityval(*nobj); /* arity */
452 PRINT_CHAR(res, fn, arg, '{');
453 WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
454 ++nobj;
455 if (i > 0) {
456 WSTACK_PUSH2(s, (UWord) nobj, PRT_LAST_ARRAY_ELEMENT+i-1);
457 }
458 break;
459 case FLOAT_DEF: {
460 FloatDef ff;
461 GET_DOUBLE(wobj, ff);
462 PRINT_DOUBLE(res, fn, arg, 'e', 6, 0, ff.fd);
463 }
464 break;
465 case BINARY_DEF:
466 {
467 byte* bytep;
468 Uint bytesize = binary_size(obj);
469 Uint bitoffs;
470 Uint bitsize;
471 byte octet;
472 ERTS_GET_BINARY_BYTES(obj, bytep, bitoffs, bitsize);
473
474 if (bitsize || !bytesize
475 || !is_printable_ascii(bytep, bytesize, bitoffs)) {
476 int is_first = 1;
477 PRINT_STRING(res, fn, arg, "<<");
478 while (bytesize) {
479 if (is_first)
480 is_first = 0;
481 else
482 PRINT_CHAR(res, fn, arg, ',');
483 if (bitoffs)
484 octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs));
485 else
486 octet = bytep[0];
487 PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet);
488 ++bytep;
489 --bytesize;
490 }
491 if (bitsize) {
492 Uint bits = bitoffs + bitsize;
493 octet = bytep[0];
494 if (bits < 8)
495 octet >>= 8 - bits;
496 else if (bits > 8) {
497 bits -= 8; /* bits in last byte */
498 octet <<= bits;
499 octet |= bytep[1] >> (8 - bits);
500 }
501 octet &= (1 << bitsize) - 1;
502 if (is_first)
503 is_first = 0;
504 else
505 PRINT_CHAR(res, fn, arg, ',');
506 PRINT_UWORD(res, fn, arg, 'u', 0, 1, octet);
507 PRINT_CHAR(res, fn, arg, ':');
508 PRINT_UWORD(res, fn, arg, 'u', 0, 1, bitsize);
509 }
510 PRINT_STRING(res, fn, arg, ">>");
511 }
512 else {
513 PRINT_STRING(res, fn, arg, "<<\"");
514 while (bytesize) {
515 if (bitoffs)
516 octet = (bytep[0] << bitoffs) | (bytep[1] >> (8-bitoffs));
517 else
518 octet = bytep[0];
519 if (octet == '"')
520 PRINT_CHAR(res, fn, arg, '\\');
521 PRINT_CHAR(res, fn, arg, octet);
522 ++bytep;
523 --bytesize;
524 }
525 PRINT_STRING(res, fn, arg, "\">>");
526 }
527 }
528 break;
529 case EXPORT_DEF:
530 {
531 Export* ep = *((Export **) (export_val(wobj) + 1));
532 Atom* module = atom_tab(atom_val(ep->info.mfa.module));
533 Atom* name = atom_tab(atom_val(ep->info.mfa.function));
534
535 PRINT_STRING(res, fn, arg, "fun ");
536 PRINT_BUF(res, fn, arg, module->name, module->len);
537 PRINT_CHAR(res, fn, arg, ':');
538 PRINT_BUF(res, fn, arg, name->name, name->len);
539 PRINT_CHAR(res, fn, arg, '/');
540 PRINT_SWORD(res, fn, arg, 'd', 0, 1,
541 (ErlPfSWord) ep->info.mfa.arity);
542 }
543 break;
544 case FUN_DEF:
545 {
546 ErlFunThing *funp = (ErlFunThing *) fun_val(wobj);
547 Atom *ap = atom_tab(atom_val(funp->fe->module));
548
549 PRINT_STRING(res, fn, arg, "#Fun<");
550 PRINT_BUF(res, fn, arg, ap->name, ap->len);
551 PRINT_CHAR(res, fn, arg, '.');
552 PRINT_SWORD(res, fn, arg, 'd', 0, 1,
553 (ErlPfSWord) funp->fe->old_index);
554 PRINT_CHAR(res, fn, arg, '.');
555 PRINT_SWORD(res, fn, arg, 'd', 0, 1,
556 (ErlPfSWord) funp->fe->old_uniq);
557 PRINT_CHAR(res, fn, arg, '>');
558 }
559 break;
560 case MAP_DEF:
561 if (is_flatmap(wobj)) {
562 Uint n;
563 Eterm *ks, *vs;
564 flatmap_t *mp = (flatmap_t *)flatmap_val(wobj);
565 n = flatmap_get_size(mp);
566 ks = flatmap_get_keys(mp);
567 vs = flatmap_get_values(mp);
568
569 PRINT_CHAR(res, fn, arg, '#');
570 PRINT_CHAR(res, fn, arg, '{');
571 WSTACK_PUSH(s, PRT_CLOSE_TUPLE);
572 if (n > 0) {
573 n--;
574 WSTACK_PUSH5(s, vs[n], PRT_TERM, PRT_ASSOC, ks[n], PRT_TERM);
575 while (n--) {
576 WSTACK_PUSH6(s, PRT_COMMA, vs[n], PRT_TERM, PRT_ASSOC,
577 ks[n], PRT_TERM);
578 }
579 }
580 } else {
581 Uint n, mapval;
582 Eterm *head;
583 head = hashmap_val(wobj);
584 mapval = MAP_HEADER_VAL(*head);
585 switch (MAP_HEADER_TYPE(*head)) {
586 case MAP_HEADER_TAG_HAMT_HEAD_ARRAY:
587 case MAP_HEADER_TAG_HAMT_HEAD_BITMAP:
588 PRINT_STRING(res, fn, arg, "#<");
589 PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval);
590 PRINT_STRING(res, fn, arg, ">{");
591 WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
592 n = hashmap_bitcount(mapval);
593 ASSERT(n < 17);
594 head += 2;
595 if (n > 0) {
596 n--;
597 WSTACK_PUSH(s, head[n]);
598 WSTACK_PUSH(s, PRT_TERM);
599 while (n--) {
600 WSTACK_PUSH(s, PRT_COMMA);
601 WSTACK_PUSH(s, head[n]);
602 WSTACK_PUSH(s, PRT_TERM);
603 }
604 }
605 break;
606 case MAP_HEADER_TAG_HAMT_NODE_BITMAP:
607 n = hashmap_bitcount(mapval);
608 head++;
609 PRINT_CHAR(res, fn, arg, '<');
610 PRINT_UWORD(res, fn, arg, 'x', 0, 1, mapval);
611 PRINT_STRING(res, fn, arg, ">{");
612 WSTACK_PUSH(s,PRT_CLOSE_TUPLE);
613 ASSERT(n < 17);
614 if (n > 0) {
615 n--;
616 WSTACK_PUSH(s, head[n]);
617 WSTACK_PUSH(s, PRT_TERM);
618 while (n--) {
619 WSTACK_PUSH(s, PRT_COMMA);
620 WSTACK_PUSH(s, head[n]);
621 WSTACK_PUSH(s, PRT_TERM);
622 }
623 }
624 break;
625 }
626 }
627 break;
628 case MATCHSTATE_DEF:
629 PRINT_STRING(res, fn, arg, "#MatchState");
630 break;
631 default:
632 PRINT_STRING(res, fn, arg, "<unknown:");
633 PRINT_POINTER(res, fn, arg, wobj);
634 PRINT_CHAR(res, fn, arg, '>');
635 break;
636 }
637 }
638
639 L_done:
640 DESTROY_WSTACK(s);
641 return res;
642 }
643
644
645 int
erts_printf_term(fmtfn_t fn,void * arg,ErlPfEterm term,long precision)646 erts_printf_term(fmtfn_t fn, void* arg, ErlPfEterm term, long precision) {
647 int res;
648 ERTS_CT_ASSERT(sizeof(ErlPfEterm) == sizeof(Eterm));
649
650 res = print_term(fn, arg, (Eterm)term, &precision);
651 if (res < 0)
652 return res;
653 if (precision <= 0)
654 PRINT_STRING(res, fn, arg, "... ");
655 return res;
656 }
657