Reify bytevector? in the correct module
[bpt/guile.git] / libguile / print.c
1 /* Copyright (C) 1995-1999, 2000, 2001, 2002, 2003, 2004, 2006, 2008,
2 * 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 3 of
7 * the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
18 */
19
20
21 \f
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <iconv.h>
28 #include <stdio.h>
29 #include <assert.h>
30
31 #include <uniconv.h>
32 #include <unictype.h>
33
34 #include "libguile/_scm.h"
35 #include "libguile/chars.h"
36 #include "libguile/continuations.h"
37 #include "libguile/smob.h"
38 #include "libguile/control.h"
39 #include "libguile/eval.h"
40 #include "libguile/macros.h"
41 #include "libguile/procprop.h"
42 #include "libguile/read.h"
43 #include "libguile/programs.h"
44 #include "libguile/alist.h"
45 #include "libguile/struct.h"
46 #include "libguile/ports.h"
47 #include "libguile/ports-internal.h"
48 #include "libguile/root.h"
49 #include "libguile/strings.h"
50 #include "libguile/strports.h"
51 #include "libguile/vectors.h"
52 #include "libguile/numbers.h"
53 #include "libguile/vm.h"
54
55 #include "libguile/validate.h"
56 #include "libguile/print.h"
57
58 #include "libguile/private-options.h"
59
60 \f
61
62 /* Character printers. */
63
64 #define PORT_CONVERSION_HANDLER(port) \
65 SCM_PTAB_ENTRY (port)->ilseq_handler
66
67 static size_t display_string (const void *, int, size_t, SCM,
68 scm_t_string_failed_conversion_handler);
69
70 static size_t write_string (const void *, int, size_t, SCM,
71 scm_t_string_failed_conversion_handler);
72
73 static int display_character (scm_t_wchar, SCM,
74 scm_t_string_failed_conversion_handler);
75
76 static void write_character (scm_t_wchar, SCM, int);
77
78 static void write_character_escaped (scm_t_wchar, int, SCM);
79
80 \f
81
82 /* {Names of immediate symbols}
83 *
84 * This table must agree with the declarations in scm.h: {Immediate Symbols}.
85 */
86
87 /* This table must agree with the list of flags in tags.h. */
88 static const char *iflagnames[] =
89 {
90 "#f",
91 "#nil", /* Elisp nil value. Should print from elisp as symbol `nil'. */
92 "#<XXX UNUSED LISP FALSE -- DO NOT USE -- SHOULD NEVER BE SEEN XXX>",
93 "()",
94 "#t",
95 "#<XXX UNUSED BOOLEAN 0 -- DO NOT USE -- SHOULD NEVER BE SEEN XXX>",
96 "#<XXX UNUSED BOOLEAN 1 -- DO NOT USE -- SHOULD NEVER BE SEEN XXX>",
97 "#<XXX UNUSED BOOLEAN 2 -- DO NOT USE -- SHOULD NEVER BE SEEN XXX>",
98 "#<unspecified>",
99 "#<undefined>",
100 "#<eof>",
101
102 /* Unbound slot marker for GOOPS. For internal use in GOOPS only. */
103 "#<unbound>",
104 };
105
106 SCM_SYMBOL (sym_reader, "reader");
107
108 scm_t_option scm_print_opts[] = {
109 { SCM_OPTION_SCM, "highlight-prefix", (scm_t_bits)SCM_BOOL_F_BITS,
110 "The string to print before highlighted values." },
111 { SCM_OPTION_SCM, "highlight-suffix", (scm_t_bits)SCM_BOOL_F_BITS,
112 "The string to print after highlighted values." },
113 { SCM_OPTION_SCM, "quote-keywordish-symbols", (scm_t_bits)SCM_BOOL_F_BITS,
114 "How to print symbols that have a colon as their first or last character. "
115 "The value '#f' does not quote the colons; '#t' quotes them; "
116 "'reader' quotes them when the reader option 'keywords' is not '#f'." },
117 { SCM_OPTION_BOOLEAN, "escape-newlines", 1,
118 "Render newlines as \\n when printing using `write'." },
119 { SCM_OPTION_BOOLEAN, "r7rs-symbols", 0,
120 "Escape symbols using R7RS |...| symbol notation." },
121 { 0 },
122 };
123
124 SCM_DEFINE (scm_print_options, "print-options-interface", 0, 1, 0,
125 (SCM setting),
126 "Option interface for the print options. Instead of using\n"
127 "this procedure directly, use the procedures\n"
128 "@code{print-enable}, @code{print-disable}, @code{print-set!}\n"
129 "and @code{print-options}.")
130 #define FUNC_NAME s_scm_print_options
131 {
132 SCM ans = scm_options (setting,
133 scm_print_opts,
134 FUNC_NAME);
135 return ans;
136 }
137 #undef FUNC_NAME
138
139 \f
140 /* {Printing of Scheme Objects}
141 */
142
143 /* Detection of circular references.
144 *
145 * Due to other constraints in the implementation, this code has bad
146 * time complexity (O (depth * N)), The printer code can be
147 * rewritten to be O(N).
148 */
149 #define PUSH_REF(pstate, obj) \
150 do \
151 { \
152 PSTATE_STACK_SET (pstate, pstate->top, obj); \
153 pstate->top++; \
154 if (pstate->top == pstate->ceiling) \
155 grow_ref_stack (pstate); \
156 } while(0)
157
158 #define ENTER_NESTED_DATA(pstate, obj, label) \
159 do \
160 { \
161 register unsigned long i; \
162 for (i = 0; i < pstate->top; ++i) \
163 if (scm_is_eq (PSTATE_STACK_REF (pstate, i), (obj))) \
164 goto label; \
165 if (pstate->fancyp) \
166 { \
167 if (pstate->top - pstate->list_offset >= pstate->level) \
168 { \
169 scm_putc_unlocked ('#', port); \
170 return; \
171 } \
172 } \
173 PUSH_REF(pstate, obj); \
174 } while(0)
175
176 #define EXIT_NESTED_DATA(pstate) \
177 do \
178 { \
179 --pstate->top; \
180 PSTATE_STACK_SET (pstate, pstate->top, SCM_UNDEFINED); \
181 } \
182 while (0)
183
184 SCM scm_print_state_vtable = SCM_BOOL_F;
185 static SCM print_state_pool = SCM_EOL;
186 scm_i_pthread_mutex_t print_state_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
187
188 #ifdef GUILE_DEBUG /* Used for debugging purposes */
189
190 SCM_DEFINE (scm_current_pstate, "current-pstate", 0, 0, 0,
191 (),
192 "Return the current-pstate -- the car of the\n"
193 "@code{print_state_pool}. @code{current-pstate} is only\n"
194 "included in @code{--enable-guile-debug} builds.")
195 #define FUNC_NAME s_scm_current_pstate
196 {
197 if (!scm_is_null (print_state_pool))
198 return SCM_CAR (print_state_pool);
199 else
200 return SCM_BOOL_F;
201 }
202 #undef FUNC_NAME
203
204 #endif
205
206 #define PSTATE_SIZE 50L
207
208 static SCM
209 make_print_state (void)
210 {
211 SCM print_state
212 = scm_make_struct (scm_print_state_vtable, SCM_INUM0, SCM_EOL);
213 scm_print_state *pstate = SCM_PRINT_STATE (print_state);
214 pstate->ref_vect = scm_c_make_vector (PSTATE_SIZE, SCM_UNDEFINED);
215 pstate->ceiling = SCM_SIMPLE_VECTOR_LENGTH (pstate->ref_vect);
216 pstate->highlight_objects = SCM_EOL;
217 return print_state;
218 }
219
220 SCM
221 scm_make_print_state ()
222 {
223 SCM answer = SCM_BOOL_F;
224
225 /* First try to allocate a print state from the pool */
226 scm_i_pthread_mutex_lock (&print_state_mutex);
227 if (!scm_is_null (print_state_pool))
228 {
229 answer = SCM_CAR (print_state_pool);
230 print_state_pool = SCM_CDR (print_state_pool);
231 }
232 scm_i_pthread_mutex_unlock (&print_state_mutex);
233
234 return scm_is_false (answer) ? make_print_state () : answer;
235 }
236
237 void
238 scm_free_print_state (SCM print_state)
239 {
240 SCM handle;
241 scm_print_state *pstate = SCM_PRINT_STATE (print_state);
242 /* Cleanup before returning print state to pool.
243 * It is better to do it here. Doing it in scm_prin1
244 * would cost more since that function is called much more
245 * often.
246 */
247 pstate->fancyp = 0;
248 pstate->revealed = 0;
249 pstate->highlight_objects = SCM_EOL;
250 scm_i_pthread_mutex_lock (&print_state_mutex);
251 handle = scm_cons (print_state, print_state_pool);
252 print_state_pool = handle;
253 scm_i_pthread_mutex_unlock (&print_state_mutex);
254 }
255
256 SCM
257 scm_i_port_with_print_state (SCM port, SCM print_state)
258 {
259 if (SCM_UNBNDP (print_state))
260 {
261 if (SCM_PORT_WITH_PS_P (port))
262 return port;
263 else
264 print_state = scm_make_print_state ();
265 /* port does not need to be coerced since it doesn't have ps */
266 }
267 else
268 port = SCM_COERCE_OUTPORT (port);
269 SCM_RETURN_NEWSMOB (scm_tc16_port_with_ps,
270 SCM_UNPACK (scm_cons (port, print_state)));
271 }
272
273 static void
274 grow_ref_stack (scm_print_state *pstate)
275 {
276 SCM old_vect = pstate->ref_vect;
277 size_t old_size = SCM_SIMPLE_VECTOR_LENGTH (old_vect);
278 size_t new_size = 2 * pstate->ceiling;
279 SCM new_vect = scm_c_make_vector (new_size, SCM_UNDEFINED);
280 unsigned long int i;
281
282 for (i = 0; i != old_size; ++i)
283 SCM_SIMPLE_VECTOR_SET (new_vect, i, SCM_SIMPLE_VECTOR_REF (old_vect, i));
284
285 pstate->ref_vect = new_vect;
286 pstate->ceiling = new_size;
287 }
288
289 #define PSTATE_STACK_REF(p,i) SCM_SIMPLE_VECTOR_REF((p)->ref_vect, (i))
290 #define PSTATE_STACK_SET(p,i,v) SCM_SIMPLE_VECTOR_SET((p)->ref_vect, (i), (v))
291
292 static void
293 print_circref (SCM port, scm_print_state *pstate, SCM ref)
294 {
295 register long i;
296 long self = pstate->top - 1;
297 i = pstate->top - 1;
298 if (scm_is_pair (PSTATE_STACK_REF (pstate, i)))
299 {
300 while (i > 0)
301 {
302 if (!scm_is_pair (PSTATE_STACK_REF (pstate, i-1))
303 || !scm_is_eq (SCM_CDR (PSTATE_STACK_REF (pstate, i-1)),
304 SCM_CDR (PSTATE_STACK_REF (pstate, i))))
305 break;
306 --i;
307 }
308 self = i;
309 }
310 for (i = pstate->top - 1; 1; --i)
311 if (scm_is_eq (PSTATE_STACK_REF(pstate, i), ref))
312 break;
313 scm_putc_unlocked ('#', port);
314 scm_intprint (i - self, 10, port);
315 scm_putc_unlocked ('#', port);
316 }
317
318 /* Print the name of a symbol. */
319
320 static int
321 quote_keywordish_symbols (void)
322 {
323 SCM option = SCM_PRINT_KEYWORD_STYLE;
324
325 if (scm_is_false (option))
326 return 0;
327 if (scm_is_eq (option, sym_reader))
328 return scm_is_true (SCM_PACK (SCM_KEYWORD_STYLE));
329 return 1;
330 }
331
332 #define INITIAL_IDENTIFIER_MASK \
333 (UC_CATEGORY_MASK_Lu | UC_CATEGORY_MASK_Ll | UC_CATEGORY_MASK_Lt \
334 | UC_CATEGORY_MASK_Lm | UC_CATEGORY_MASK_Lo | UC_CATEGORY_MASK_Mn \
335 | UC_CATEGORY_MASK_Nl | UC_CATEGORY_MASK_No | UC_CATEGORY_MASK_Pd \
336 | UC_CATEGORY_MASK_Pc | UC_CATEGORY_MASK_Po | UC_CATEGORY_MASK_Sc \
337 | UC_CATEGORY_MASK_Sm | UC_CATEGORY_MASK_Sk | UC_CATEGORY_MASK_So \
338 | UC_CATEGORY_MASK_Co)
339
340 #define SUBSEQUENT_IDENTIFIER_MASK \
341 (INITIAL_IDENTIFIER_MASK \
342 | UC_CATEGORY_MASK_Nd | UC_CATEGORY_MASK_Mc | UC_CATEGORY_MASK_Me)
343
344 /* FIXME: Cache this information on the symbol, somehow. */
345 static int
346 symbol_has_extended_read_syntax (SCM sym)
347 {
348 size_t pos, len = scm_i_symbol_length (sym);
349 scm_t_wchar c;
350
351 /* The empty symbol. */
352 if (len == 0)
353 return 1;
354
355 c = scm_i_symbol_ref (sym, 0);
356
357 switch (c)
358 {
359 case '\'':
360 case '`':
361 case ',':
362 case '"':
363 case ';':
364 case '#':
365 /* Some initial-character constraints. */
366 return 1;
367
368 case '|':
369 case '\\':
370 /* R7RS allows neither '|' nor '\' in bare symbols. */
371 if (SCM_PRINT_R7RS_SYMBOLS_P)
372 return 1;
373 break;
374
375 case ':':
376 /* Symbols that look like keywords. */
377 return quote_keywordish_symbols ();
378
379 case '.':
380 /* Single dot conflicts with dotted-pair notation. */
381 if (len == 1)
382 return 1;
383 /* Fall through to check numbers. */
384 case '+':
385 case '-':
386 case '0':
387 case '1':
388 case '2':
389 case '3':
390 case '4':
391 case '5':
392 case '6':
393 case '7':
394 case '8':
395 case '9':
396 /* Number-ish symbols. Numbers with radixes already caught be #
397 above. */
398 if (scm_is_true (scm_i_string_to_number (scm_symbol_to_string (sym), 10)))
399 return 1;
400 break;
401
402 default:
403 break;
404 }
405
406 /* Other disallowed first characters. */
407 if (!uc_is_general_category_withtable (c, INITIAL_IDENTIFIER_MASK))
408 return 1;
409
410 /* Keywords can be identified by trailing colons too. */
411 if (scm_i_symbol_ref (sym, len - 1) == ':')
412 return quote_keywordish_symbols ();
413
414 /* Otherwise, any character that's in the identifier category mask is
415 fine to pass through as-is, provided it's not one of the ASCII
416 delimiters like `;'. */
417 for (pos = 1; pos < len; pos++)
418 {
419 c = scm_i_symbol_ref (sym, pos);
420 if (!uc_is_general_category_withtable (c, SUBSEQUENT_IDENTIFIER_MASK))
421 return 1;
422 else if (c == '"' || c == ';' || c == '#')
423 return 1;
424 else if ((c == '|' || c == '\\') && SCM_PRINT_R7RS_SYMBOLS_P)
425 /* R7RS allows neither '|' nor '\' in bare symbols. */
426 return 1;
427 }
428
429 return 0;
430 }
431
432 static void
433 print_normal_symbol (SCM sym, SCM port)
434 {
435 size_t len;
436 scm_t_string_failed_conversion_handler strategy;
437
438 len = scm_i_symbol_length (sym);
439 strategy = SCM_PTAB_ENTRY (port)->ilseq_handler;
440
441 if (scm_i_is_narrow_symbol (sym))
442 display_string (scm_i_symbol_chars (sym), 1, len, port, strategy);
443 else
444 display_string (scm_i_symbol_wide_chars (sym), 0, len, port, strategy);
445 }
446
447 static void
448 print_extended_symbol (SCM sym, SCM port)
449 {
450 size_t pos, len;
451 scm_t_string_failed_conversion_handler strategy;
452
453 len = scm_i_symbol_length (sym);
454 strategy = PORT_CONVERSION_HANDLER (port);
455
456 scm_lfwrite_unlocked ("#{", 2, port);
457
458 for (pos = 0; pos < len; pos++)
459 {
460 scm_t_wchar c = scm_i_symbol_ref (sym, pos);
461
462 if (uc_is_general_category_withtable (c,
463 SUBSEQUENT_IDENTIFIER_MASK
464 | UC_CATEGORY_MASK_Zs))
465 {
466 if (!display_character (c, port, strategy)
467 || (c == '\\' && !display_character (c, port, strategy)))
468 scm_encoding_error ("print_extended_symbol", errno,
469 "cannot convert to output locale",
470 port, SCM_MAKE_CHAR (c));
471 }
472 else
473 {
474 scm_lfwrite_unlocked ("\\x", 2, port);
475 scm_intprint (c, 16, port);
476 scm_putc_unlocked (';', port);
477 }
478 }
479
480 scm_lfwrite_unlocked ("}#", 2, port);
481 }
482
483 static void
484 print_r7rs_extended_symbol (SCM sym, SCM port)
485 {
486 size_t pos, len;
487 scm_t_string_failed_conversion_handler strategy;
488
489 len = scm_i_symbol_length (sym);
490 strategy = PORT_CONVERSION_HANDLER (port);
491
492 scm_putc_unlocked ('|', port);
493
494 for (pos = 0; pos < len; pos++)
495 {
496 scm_t_wchar c = scm_i_symbol_ref (sym, pos);
497
498 switch (c)
499 {
500 case '\a': scm_lfwrite_unlocked ("\\a", 2, port); break;
501 case '\b': scm_lfwrite_unlocked ("\\b", 2, port); break;
502 case '\t': scm_lfwrite_unlocked ("\\t", 2, port); break;
503 case '\n': scm_lfwrite_unlocked ("\\n", 2, port); break;
504 case '\r': scm_lfwrite_unlocked ("\\r", 2, port); break;
505 case '|': scm_lfwrite_unlocked ("\\|", 2, port); break;
506 case '\\': scm_lfwrite_unlocked ("\\x5c;", 5, port); break;
507 default:
508 if (uc_is_general_category_withtable (c,
509 UC_CATEGORY_MASK_L
510 | UC_CATEGORY_MASK_M
511 | UC_CATEGORY_MASK_N
512 | UC_CATEGORY_MASK_P
513 | UC_CATEGORY_MASK_S)
514 || (c == ' '))
515 {
516 if (!display_character (c, port, strategy))
517 scm_encoding_error ("print_r7rs_extended_symbol", errno,
518 "cannot convert to output locale",
519 port, SCM_MAKE_CHAR (c));
520 }
521 else
522 {
523 scm_lfwrite_unlocked ("\\x", 2, port);
524 scm_intprint (c, 16, port);
525 scm_putc_unlocked (';', port);
526 }
527 break;
528 }
529 }
530
531 scm_putc_unlocked ('|', port);
532 }
533
534 /* FIXME: allow R6RS hex escapes instead of #{...}# or |...|. */
535 static void
536 print_symbol (SCM sym, SCM port)
537 {
538 if (!symbol_has_extended_read_syntax (sym))
539 print_normal_symbol (sym, port);
540 else if (SCM_PRINT_R7RS_SYMBOLS_P)
541 print_r7rs_extended_symbol (sym, port);
542 else
543 print_extended_symbol (sym, port);
544 }
545
546 void
547 scm_print_symbol_name (const char *str, size_t len, SCM port)
548 {
549 SCM symbol = scm_from_utf8_symboln (str, len);
550 print_symbol (symbol, port);
551 }
552
553 /* Print generally. Handles both write and display according to PSTATE.
554 */
555 SCM_GPROC(s_write, "write", 1, 1, 0, scm_write, g_write);
556 SCM_GPROC(s_display, "display", 1, 1, 0, scm_display, g_display);
557
558 static void iprin1 (SCM exp, SCM port, scm_print_state *pstate);
559
560
561 /* Print a character as an octal or hex escape. */
562 #define PRINT_CHAR_ESCAPE(i, port) \
563 do \
564 { \
565 if (!SCM_R6RS_ESCAPES_P) \
566 scm_intprint (i, 8, port); \
567 else \
568 { \
569 scm_puts_unlocked ("x", port); \
570 scm_intprint (i, 16, port); \
571 } \
572 } \
573 while (0)
574
575
576 void
577 scm_iprin1 (SCM exp, SCM port, scm_print_state *pstate)
578 {
579 if (pstate->fancyp
580 && scm_is_true (scm_memq (exp, pstate->highlight_objects)))
581 {
582 scm_display (SCM_PRINT_HIGHLIGHT_PREFIX, port);
583 iprin1 (exp, port, pstate);
584 scm_display (SCM_PRINT_HIGHLIGHT_SUFFIX, port);
585 }
586 else
587 iprin1 (exp, port, pstate);
588 }
589
590 static void
591 print_vector_or_weak_vector (SCM v, size_t len, SCM (*ref) (SCM, size_t),
592 SCM port, scm_print_state *pstate)
593 {
594 long i;
595 long last = len - 1;
596 int cutp = 0;
597 if (pstate->fancyp && len > pstate->length)
598 {
599 last = pstate->length - 1;
600 cutp = 1;
601 }
602 for (i = 0; i < last; ++i)
603 {
604 scm_iprin1 (ref (v, i), port, pstate);
605 scm_putc_unlocked (' ', port);
606 }
607 if (i == last)
608 {
609 /* CHECK_INTS; */
610 scm_iprin1 (ref (v, i), port, pstate);
611 }
612 if (cutp)
613 scm_puts_unlocked (" ...", port);
614 scm_putc_unlocked (')', port);
615 }
616
617 static void
618 iprin1 (SCM exp, SCM port, scm_print_state *pstate)
619 {
620 switch (SCM_ITAG3 (exp))
621 {
622 case scm_tc3_tc7_1:
623 case scm_tc3_tc7_2:
624 /* These tc3 tags should never occur in an immediate value. They are
625 * only used in cell types of non-immediates, i. e. the value returned
626 * by SCM_CELL_TYPE (exp) can use these tags.
627 */
628 scm_ipruk ("immediate", exp, port);
629 break;
630 case scm_tc3_int_1:
631 case scm_tc3_int_2:
632 scm_intprint (SCM_I_INUM (exp), 10, port);
633 break;
634 case scm_tc3_imm24:
635 if (SCM_CHARP (exp))
636 {
637 if (SCM_WRITINGP (pstate))
638 write_character (SCM_CHAR (exp), port, 0);
639 else
640 {
641 if (!display_character (SCM_CHAR (exp), port,
642 PORT_CONVERSION_HANDLER (port)))
643 scm_encoding_error (__func__, errno,
644 "cannot convert to output locale",
645 port, exp);
646 }
647 }
648 else if (SCM_IFLAGP (exp)
649 && ((size_t) SCM_IFLAGNUM (exp) < (sizeof iflagnames / sizeof (char *))))
650 {
651 scm_puts_unlocked (iflagnames [SCM_IFLAGNUM (exp)], port);
652 }
653 else
654 {
655 /* unknown immediate value */
656 scm_ipruk ("immediate", exp, port);
657 }
658 break;
659 case scm_tc3_cons:
660 switch (SCM_TYP7 (exp))
661 {
662 case scm_tcs_struct:
663 {
664 ENTER_NESTED_DATA (pstate, exp, circref);
665 if (SCM_OBJ_CLASS_FLAGS (exp) & SCM_CLASSF_GOOPS)
666 {
667 SCM pwps, print = pstate->writingp ? g_write : g_display;
668 if (SCM_UNPACK (print) == 0)
669 goto print_struct;
670 pwps = scm_i_port_with_print_state (port, pstate->handle);
671 pstate->revealed = 1;
672 scm_call_2 (print, exp, pwps);
673 }
674 else
675 {
676 print_struct:
677 scm_print_struct (exp, port, pstate);
678 }
679 EXIT_NESTED_DATA (pstate);
680 }
681 break;
682 case scm_tcs_cons_imcar:
683 case scm_tcs_cons_nimcar:
684 ENTER_NESTED_DATA (pstate, exp, circref);
685 scm_iprlist ("(", exp, ')', port, pstate);
686 EXIT_NESTED_DATA (pstate);
687 break;
688 circref:
689 print_circref (port, pstate, exp);
690 break;
691 case scm_tc7_number:
692 switch SCM_TYP16 (exp) {
693 case scm_tc16_big:
694 scm_bigprint (exp, port, pstate);
695 break;
696 case scm_tc16_real:
697 scm_print_real (exp, port, pstate);
698 break;
699 case scm_tc16_complex:
700 scm_print_complex (exp, port, pstate);
701 break;
702 case scm_tc16_fraction:
703 scm_i_print_fraction (exp, port, pstate);
704 break;
705 }
706 break;
707 case scm_tc7_stringbuf:
708 scm_i_print_stringbuf (exp, port, pstate);
709 break;
710 case scm_tc7_string:
711 {
712 size_t len, printed;
713
714 len = scm_i_string_length (exp);
715 if (SCM_WRITINGP (pstate))
716 {
717 printed = write_string (scm_i_string_data (exp),
718 scm_i_is_narrow_string (exp),
719 len, port,
720 PORT_CONVERSION_HANDLER (port));
721 len += 2; /* account for the quotes */
722 }
723 else
724 printed = display_string (scm_i_string_data (exp),
725 scm_i_is_narrow_string (exp),
726 len, port,
727 PORT_CONVERSION_HANDLER (port));
728
729 if (SCM_UNLIKELY (printed < len))
730 scm_encoding_error (__func__, errno,
731 "cannot convert to output locale",
732 port, scm_c_string_ref (exp, printed));
733 }
734
735 scm_remember_upto_here_1 (exp);
736 break;
737 case scm_tc7_symbol:
738 if (scm_i_symbol_is_interned (exp))
739 {
740 print_symbol (exp, port);
741 scm_remember_upto_here_1 (exp);
742 }
743 else
744 {
745 scm_puts_unlocked ("#<uninterned-symbol ", port);
746 print_symbol (exp, port);
747 scm_putc_unlocked (' ', port);
748 scm_uintprint (SCM_UNPACK (exp), 16, port);
749 scm_putc_unlocked ('>', port);
750 }
751 break;
752 case scm_tc7_variable:
753 scm_i_variable_print (exp, port, pstate);
754 break;
755 case scm_tc7_program:
756 scm_i_program_print (exp, port, pstate);
757 break;
758 case scm_tc7_pointer:
759 scm_i_pointer_print (exp, port, pstate);
760 break;
761 case scm_tc7_hashtable:
762 scm_i_hashtable_print (exp, port, pstate);
763 break;
764 case scm_tc7_weak_set:
765 scm_i_weak_set_print (exp, port, pstate);
766 break;
767 case scm_tc7_weak_table:
768 scm_i_weak_table_print (exp, port, pstate);
769 break;
770 case scm_tc7_fluid:
771 scm_i_fluid_print (exp, port, pstate);
772 break;
773 case scm_tc7_dynamic_state:
774 scm_i_dynamic_state_print (exp, port, pstate);
775 break;
776 case scm_tc7_frame:
777 scm_i_frame_print (exp, port, pstate);
778 break;
779 case scm_tc7_keyword:
780 scm_puts_unlocked ("#:", port);
781 scm_iprin1 (scm_keyword_to_symbol (exp), port, pstate);
782 break;
783 case scm_tc7_vm_cont:
784 scm_i_vm_cont_print (exp, port, pstate);
785 break;
786 case scm_tc7_array:
787 ENTER_NESTED_DATA (pstate, exp, circref);
788 scm_i_print_array (exp, port, pstate);
789 EXIT_NESTED_DATA (pstate);
790 break;
791 case scm_tc7_bytevector:
792 scm_i_print_bytevector (exp, port, pstate);
793 break;
794 case scm_tc7_bitvector:
795 scm_i_print_bitvector (exp, port, pstate);
796 break;
797 case scm_tc7_wvect:
798 ENTER_NESTED_DATA (pstate, exp, circref);
799 scm_puts_unlocked ("#w(", port);
800 print_vector_or_weak_vector (exp, scm_c_weak_vector_length (exp),
801 scm_c_weak_vector_ref, port, pstate);
802 EXIT_NESTED_DATA (pstate);
803 break;
804 case scm_tc7_vector:
805 ENTER_NESTED_DATA (pstate, exp, circref);
806 scm_puts_unlocked ("#(", port);
807 print_vector_or_weak_vector (exp, SCM_SIMPLE_VECTOR_LENGTH (exp),
808 scm_c_vector_ref, port, pstate);
809 EXIT_NESTED_DATA (pstate);
810 break;
811 case scm_tc7_port:
812 {
813 scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (exp);
814 if (ptob->print && ptob->print (exp, port, pstate))
815 break;
816 goto punk;
817 }
818 case scm_tc7_smob:
819 ENTER_NESTED_DATA (pstate, exp, circref);
820 SCM_SMOB_DESCRIPTOR (exp).print (exp, port, pstate);
821 EXIT_NESTED_DATA (pstate);
822 break;
823 default:
824 /* case scm_tcs_closures: */
825 punk:
826 scm_ipruk ("type", exp, port);
827 }
828 }
829 }
830
831 /* Print states are necessary for circular reference safe printing.
832 * They are also expensive to allocate. Therefore print states are
833 * kept in a pool so that they can be reused.
834 */
835
836 /* The PORT argument can also be a print-state/port pair, which will
837 * then be used instead of allocating a new print state. This is
838 * useful for continuing a chain of print calls from Scheme. */
839
840 void
841 scm_prin1 (SCM exp, SCM port, int writingp)
842 {
843 SCM handle = SCM_BOOL_F; /* Will GC protect the handle whilst unlinked */
844 SCM pstate_scm;
845 scm_print_state *pstate;
846 int old_writingp;
847
848 /* If PORT is a print-state/port pair, use that. Else create a new
849 print-state. */
850
851 if (SCM_PORT_WITH_PS_P (port))
852 {
853 pstate_scm = SCM_PORT_WITH_PS_PS (port);
854 port = SCM_PORT_WITH_PS_PORT (port);
855 }
856 else
857 {
858 /* First try to allocate a print state from the pool */
859 scm_i_pthread_mutex_lock (&print_state_mutex);
860 if (!scm_is_null (print_state_pool))
861 {
862 handle = print_state_pool;
863 print_state_pool = SCM_CDR (print_state_pool);
864 }
865 scm_i_pthread_mutex_unlock (&print_state_mutex);
866 if (scm_is_false (handle))
867 handle = scm_list_1 (make_print_state ());
868 pstate_scm = SCM_CAR (handle);
869 }
870
871 pstate = SCM_PRINT_STATE (pstate_scm);
872 old_writingp = pstate->writingp;
873 pstate->writingp = writingp;
874 scm_iprin1 (exp, port, pstate);
875 pstate->writingp = old_writingp;
876
877 /* Return print state to pool if it has been created above and
878 hasn't escaped to Scheme. */
879
880 if (scm_is_true (handle) && !pstate->revealed)
881 {
882 scm_i_pthread_mutex_lock (&print_state_mutex);
883 SCM_SETCDR (handle, print_state_pool);
884 print_state_pool = handle;
885 scm_i_pthread_mutex_unlock (&print_state_mutex);
886 }
887 }
888
889 /* Convert codepoint CH to UTF-8 and store the result in UTF8. Return
890 the number of bytes of the UTF-8-encoded string. */
891 static size_t
892 codepoint_to_utf8 (scm_t_wchar ch, scm_t_uint8 utf8[4])
893 {
894 size_t len;
895 scm_t_uint32 codepoint;
896
897 codepoint = (scm_t_uint32) ch;
898
899 if (codepoint <= 0x7f)
900 {
901 len = 1;
902 utf8[0] = (scm_t_uint8) codepoint;
903 }
904 else if (codepoint <= 0x7ffUL)
905 {
906 len = 2;
907 utf8[0] = 0xc0 | (codepoint >> 6);
908 utf8[1] = 0x80 | (codepoint & 0x3f);
909 }
910 else if (codepoint <= 0xffffUL)
911 {
912 len = 3;
913 utf8[0] = 0xe0 | (codepoint >> 12);
914 utf8[1] = 0x80 | ((codepoint >> 6) & 0x3f);
915 utf8[2] = 0x80 | (codepoint & 0x3f);
916 }
917 else
918 {
919 len = 4;
920 utf8[0] = 0xf0 | (codepoint >> 18);
921 utf8[1] = 0x80 | ((codepoint >> 12) & 0x3f);
922 utf8[2] = 0x80 | ((codepoint >> 6) & 0x3f);
923 utf8[3] = 0x80 | (codepoint & 0x3f);
924 }
925
926 return len;
927 }
928
929 #define STR_REF(s, x) \
930 (narrow_p \
931 ? (scm_t_wchar) ((unsigned char *) (s))[x] \
932 : ((scm_t_wchar *) (s))[x])
933
934 /* Write STR to PORT as UTF-8. STR is a LEN-codepoint string; it is
935 narrow if NARROW_P is true, wide otherwise. Return LEN. */
936 static size_t
937 display_string_as_utf8 (const void *str, int narrow_p, size_t len,
938 SCM port)
939 {
940 size_t printed = 0;
941
942 while (len > printed)
943 {
944 size_t utf8_len, i;
945 char *input, utf8_buf[256];
946
947 /* Convert STR to UTF-8. */
948 for (i = printed, utf8_len = 0, input = utf8_buf;
949 i < len && utf8_len + 4 < sizeof (utf8_buf);
950 i++)
951 {
952 utf8_len += codepoint_to_utf8 (STR_REF (str, i),
953 (scm_t_uint8 *) input);
954 input = utf8_buf + utf8_len;
955 }
956
957 /* INPUT was successfully converted, entirely; print the
958 result. */
959 scm_lfwrite_unlocked (utf8_buf, utf8_len, port);
960 printed += i - printed;
961 }
962
963 assert (printed == len);
964
965 return len;
966 }
967
968 /* Write STR to PORT as ISO-8859-1. STR is a LEN-codepoint string; it
969 is narrow if NARROW_P is true, wide otherwise. Return LEN. */
970 static size_t
971 display_string_as_latin1 (const void *str, int narrow_p, size_t len,
972 SCM port,
973 scm_t_string_failed_conversion_handler strategy)
974 {
975 size_t printed = 0;
976
977 if (narrow_p)
978 {
979 scm_lfwrite_unlocked (str, len, port);
980 return len;
981 }
982
983 while (printed < len)
984 {
985 char buf[256];
986 size_t i;
987
988 for (i = 0; i < sizeof(buf) && printed < len; i++, printed++)
989 {
990 scm_t_wchar c = STR_REF (str, printed);
991
992 if (c < 256)
993 buf[i] = c;
994 else
995 break;
996 }
997
998 scm_lfwrite_unlocked (buf, i, port);
999
1000 if (i < sizeof(buf) && printed < len)
1001 {
1002 if (strategy == SCM_FAILED_CONVERSION_ERROR)
1003 break;
1004 else if (strategy == SCM_FAILED_CONVERSION_ESCAPE_SEQUENCE)
1005 write_character_escaped (STR_REF (str, printed), 1, port);
1006 else
1007 /* STRATEGY is `SCM_FAILED_CONVERSION_QUESTION_MARK'. */
1008 display_string ("?", 1, 1, port, strategy);
1009 printed++;
1010 }
1011 }
1012
1013 return printed;
1014 }
1015
1016 /* Convert STR through PORT's output conversion descriptor and write the
1017 output to PORT. Return the number of codepoints written. */
1018 static size_t
1019 display_string_using_iconv (const void *str, int narrow_p, size_t len,
1020 SCM port,
1021 scm_t_string_failed_conversion_handler strategy)
1022 {
1023 size_t printed;
1024 scm_t_iconv_descriptors *id;
1025 scm_t_port_internal *pti = SCM_PORT_GET_INTERNAL (port);
1026
1027 id = scm_i_port_iconv_descriptors (port, SCM_PORT_WRITE);
1028
1029 if (SCM_UNLIKELY (pti->at_stream_start_for_bom_write && len > 0))
1030 {
1031 scm_t_port *pt = SCM_PTAB_ENTRY (port);
1032
1033 /* Record that we're no longer at stream start. */
1034 pti->at_stream_start_for_bom_write = 0;
1035 if (pt->rw_random)
1036 pti->at_stream_start_for_bom_read = 0;
1037
1038 /* Write a BOM if appropriate. */
1039 if (SCM_UNLIKELY (strcmp(pt->encoding, "UTF-16") == 0
1040 || strcmp(pt->encoding, "UTF-32") == 0))
1041 display_character (SCM_UNICODE_BOM, port, iconveh_error);
1042 }
1043
1044 printed = 0;
1045
1046 while (len > printed)
1047 {
1048 size_t done, utf8_len, input_left, output_left, i;
1049 size_t codepoints_read, output_len;
1050 char *input, *output;
1051 char utf8_buf[256], encoded_output[256];
1052 size_t offsets[256];
1053
1054 /* Convert STR to UTF-8. */
1055 for (i = printed, utf8_len = 0, input = utf8_buf;
1056 i < len && utf8_len + 4 < sizeof (utf8_buf);
1057 i++)
1058 {
1059 offsets[utf8_len] = i;
1060 utf8_len += codepoint_to_utf8 (STR_REF (str, i),
1061 (scm_t_uint8 *) input);
1062 input = utf8_buf + utf8_len;
1063 }
1064
1065 input = utf8_buf;
1066 input_left = utf8_len;
1067
1068 output = encoded_output;
1069 output_left = sizeof (encoded_output);
1070
1071 done = iconv (id->output_cd, &input, &input_left,
1072 &output, &output_left);
1073
1074 output_len = sizeof (encoded_output) - output_left;
1075
1076 if (SCM_UNLIKELY (done == (size_t) -1))
1077 {
1078 int errno_save = errno;
1079
1080 /* Reset the `iconv' state. */
1081 iconv (id->output_cd, NULL, NULL, NULL, NULL);
1082
1083 /* Print the OUTPUT_LEN bytes successfully converted. */
1084 scm_lfwrite_unlocked (encoded_output, output_len, port);
1085
1086 /* See how many input codepoints these OUTPUT_LEN bytes
1087 corresponds to. */
1088 codepoints_read = offsets[input - utf8_buf] - printed;
1089 printed += codepoints_read;
1090
1091 if (errno_save == EILSEQ &&
1092 strategy != SCM_FAILED_CONVERSION_ERROR)
1093 {
1094 /* Conversion failed somewhere in INPUT and we want to
1095 escape or substitute the offending input character. */
1096
1097 if (strategy == SCM_FAILED_CONVERSION_ESCAPE_SEQUENCE)
1098 {
1099 scm_t_wchar ch;
1100
1101 /* Find CH, the offending codepoint, and escape it. */
1102 ch = STR_REF (str, offsets[input - utf8_buf]);
1103 write_character_escaped (ch, 1, port);
1104 }
1105 else
1106 /* STRATEGY is `SCM_FAILED_CONVERSION_QUESTION_MARK'. */
1107 display_string ("?", 1, 1, port, strategy);
1108
1109 printed++;
1110 }
1111 else
1112 /* Something bad happened that we can't handle: bail out. */
1113 break;
1114 }
1115 else
1116 {
1117 /* INPUT was successfully converted, entirely; print the
1118 result. */
1119 scm_lfwrite_unlocked (encoded_output, output_len, port);
1120 codepoints_read = i - printed;
1121 printed += codepoints_read;
1122 }
1123 }
1124
1125 return printed;
1126 }
1127
1128 /* Display the LEN codepoints in STR to PORT according to STRATEGY;
1129 return the number of codepoints successfully displayed. If NARROW_P,
1130 then STR is interpreted as a sequence of `char', denoting a Latin-1
1131 string; otherwise it's interpreted as a sequence of
1132 `scm_t_wchar'. */
1133 static size_t
1134 display_string (const void *str, int narrow_p,
1135 size_t len, SCM port,
1136 scm_t_string_failed_conversion_handler strategy)
1137 {
1138 scm_t_port_internal *pti;
1139
1140 pti = SCM_PORT_GET_INTERNAL (port);
1141
1142 if (pti->encoding_mode == SCM_PORT_ENCODING_MODE_UTF8)
1143 return display_string_as_utf8 (str, narrow_p, len, port);
1144 else if (pti->encoding_mode == SCM_PORT_ENCODING_MODE_LATIN1)
1145 return display_string_as_latin1 (str, narrow_p, len, port, strategy);
1146 else
1147 return display_string_using_iconv (str, narrow_p, len, port, strategy);
1148 }
1149
1150 /* Attempt to display CH to PORT according to STRATEGY. Return one if
1151 CH was successfully displayed, zero otherwise (e.g., if it was not
1152 representable in PORT's encoding.) */
1153 static int
1154 display_character (scm_t_wchar ch, SCM port,
1155 scm_t_string_failed_conversion_handler strategy)
1156 {
1157 return display_string (&ch, 0, 1, port, strategy) == 1;
1158 }
1159
1160 /* Same as 'display_string', but using the 'write' syntax. */
1161 static size_t
1162 write_string (const void *str, int narrow_p,
1163 size_t len, SCM port,
1164 scm_t_string_failed_conversion_handler strategy)
1165 {
1166 size_t printed;
1167
1168 printed = display_character ('"', port, strategy);
1169
1170 if (printed > 0)
1171 {
1172 size_t i;
1173
1174 for (i = 0; i < len; ++i)
1175 {
1176 write_character (STR_REF (str, i), port, 1);
1177 printed++;
1178 }
1179
1180 printed += display_character ('"', port, strategy);
1181 }
1182
1183 return printed;
1184 }
1185
1186 #undef STR_REF
1187
1188 /* Attempt to pretty-print CH, a combining character, to PORT. Return
1189 zero upon failure, non-zero otherwise. The idea is to print CH above
1190 a dotted circle to make it more visible. */
1191 static int
1192 write_combining_character (scm_t_wchar ch, SCM port)
1193 {
1194 scm_t_wchar str[2];
1195
1196 str[0] = SCM_CODEPOINT_DOTTED_CIRCLE;
1197 str[1] = ch;
1198
1199 return display_string (str, 0, 2, port, iconveh_error) == 2;
1200 }
1201
1202 /* Write CH to PORT in its escaped form, using the string escape syntax
1203 if STRING_ESCAPES_P is non-zero. */
1204 static void
1205 write_character_escaped (scm_t_wchar ch, int string_escapes_p, SCM port)
1206 {
1207 if (string_escapes_p)
1208 {
1209 /* Represent CH using the in-string escape syntax. */
1210
1211 static const char hex[] = "0123456789abcdef";
1212 static const char escapes[7] = "abtnvfr";
1213 char buf[9];
1214
1215 if (ch >= 0x07 && ch <= 0x0D && ch != 0x0A)
1216 {
1217 /* Use special escapes for some C0 controls. */
1218 buf[0] = '\\';
1219 buf[1] = escapes[ch - 0x07];
1220 scm_lfwrite_unlocked (buf, 2, port);
1221 }
1222 else if (!SCM_R6RS_ESCAPES_P)
1223 {
1224 if (ch <= 0xFF)
1225 {
1226 buf[0] = '\\';
1227 buf[1] = 'x';
1228 buf[2] = hex[ch / 16];
1229 buf[3] = hex[ch % 16];
1230 scm_lfwrite_unlocked (buf, 4, port);
1231 }
1232 else if (ch <= 0xFFFF)
1233 {
1234 buf[0] = '\\';
1235 buf[1] = 'u';
1236 buf[2] = hex[(ch & 0xF000) >> 12];
1237 buf[3] = hex[(ch & 0xF00) >> 8];
1238 buf[4] = hex[(ch & 0xF0) >> 4];
1239 buf[5] = hex[(ch & 0xF)];
1240 scm_lfwrite_unlocked (buf, 6, port);
1241 }
1242 else if (ch > 0xFFFF)
1243 {
1244 buf[0] = '\\';
1245 buf[1] = 'U';
1246 buf[2] = hex[(ch & 0xF00000) >> 20];
1247 buf[3] = hex[(ch & 0xF0000) >> 16];
1248 buf[4] = hex[(ch & 0xF000) >> 12];
1249 buf[5] = hex[(ch & 0xF00) >> 8];
1250 buf[6] = hex[(ch & 0xF0) >> 4];
1251 buf[7] = hex[(ch & 0xF)];
1252 scm_lfwrite_unlocked (buf, 8, port);
1253 }
1254 }
1255 else
1256 {
1257 /* Print an R6RS variable-length hex escape: "\xNNNN;". */
1258 scm_t_wchar ch2 = ch;
1259
1260 int i = 8;
1261 buf[i] = ';';
1262 i --;
1263 if (ch == 0)
1264 buf[i--] = '0';
1265 else
1266 while (ch2 > 0)
1267 {
1268 buf[i] = hex[ch2 & 0xF];
1269 ch2 >>= 4;
1270 i --;
1271 }
1272 buf[i] = 'x';
1273 i --;
1274 buf[i] = '\\';
1275 scm_lfwrite_unlocked (buf + i, 9 - i, port);
1276 }
1277 }
1278 else
1279 {
1280 /* Represent CH using the character escape syntax. */
1281 const char *name;
1282
1283 name = scm_i_charname (SCM_MAKE_CHAR (ch));
1284 if (name != NULL)
1285 scm_puts_unlocked (name, port);
1286 else
1287 PRINT_CHAR_ESCAPE (ch, port);
1288 }
1289 }
1290
1291 /* Write CH to PORT, escaping it if it's non-graphic or not
1292 representable in PORT's encoding. If STRING_ESCAPES_P is true and CH
1293 needs to be escaped, it is escaped using the in-string escape syntax;
1294 otherwise the character escape syntax is used. */
1295 static void
1296 write_character (scm_t_wchar ch, SCM port, int string_escapes_p)
1297 {
1298 int printed = 0;
1299 scm_t_string_failed_conversion_handler strategy;
1300
1301 strategy = PORT_CONVERSION_HANDLER (port);
1302
1303 if (string_escapes_p)
1304 {
1305 /* Check if CH deserves special treatment. */
1306 if (ch == '"' || ch == '\\')
1307 {
1308 display_character ('\\', port, iconveh_question_mark);
1309 display_character (ch, port, strategy);
1310 printed = 1;
1311 }
1312 else if (ch == '\n' && SCM_PRINT_ESCAPE_NEWLINES_P)
1313 {
1314 display_character ('\\', port, iconveh_question_mark);
1315 display_character ('n', port, strategy);
1316 printed = 1;
1317 }
1318 else if (ch == ' ' || ch == '\n')
1319 {
1320 display_character (ch, port, strategy);
1321 printed = 1;
1322 }
1323 }
1324 else
1325 {
1326 display_string ("#\\", 1, 2, port, iconveh_question_mark);
1327
1328 if (uc_combining_class (ch) != UC_CCC_NR)
1329 /* Character is a combining character, so attempt to
1330 pretty-print it. */
1331 printed = write_combining_character (ch, port);
1332 }
1333
1334 if (!printed
1335 && uc_is_general_category_withtable (ch,
1336 UC_CATEGORY_MASK_L |
1337 UC_CATEGORY_MASK_M |
1338 UC_CATEGORY_MASK_N |
1339 UC_CATEGORY_MASK_P |
1340 UC_CATEGORY_MASK_S))
1341 /* CH is graphic; attempt to display it. */
1342 printed = display_character (ch, port, iconveh_error);
1343
1344 if (!printed)
1345 /* CH isn't graphic or cannot be represented in PORT's encoding. */
1346 write_character_escaped (ch, string_escapes_p, port);
1347 }
1348
1349 /* Display STR to PORT from START inclusive to END exclusive. */
1350 void
1351 scm_i_display_substring (SCM str, size_t start, size_t end, SCM port)
1352 {
1353 int narrow_p;
1354 const char *buf;
1355 size_t len, printed;
1356
1357 buf = scm_i_string_data (str);
1358 len = end - start;
1359 narrow_p = scm_i_is_narrow_string (str);
1360 buf += start * (narrow_p ? sizeof (char) : sizeof (scm_t_wchar));
1361
1362 printed = display_string (buf, narrow_p, end - start, port,
1363 PORT_CONVERSION_HANDLER (port));
1364
1365 if (SCM_UNLIKELY (printed < len))
1366 scm_encoding_error (__func__, errno,
1367 "cannot convert to output locale",
1368 port, scm_c_string_ref (str, printed + start));
1369 }
1370
1371 \f
1372 /* Print an integer.
1373 */
1374
1375 void
1376 scm_intprint (scm_t_intmax n, int radix, SCM port)
1377 {
1378 char num_buf[SCM_INTBUFLEN];
1379 scm_lfwrite_unlocked (num_buf, scm_iint2str (n, radix, num_buf), port);
1380 }
1381
1382 void
1383 scm_uintprint (scm_t_uintmax n, int radix, SCM port)
1384 {
1385 char num_buf[SCM_INTBUFLEN];
1386 scm_lfwrite_unlocked (num_buf, scm_iuint2str (n, radix, num_buf), port);
1387 }
1388
1389 /* Print an object of unrecognized type.
1390 */
1391
1392 void
1393 scm_ipruk (char *hdr, SCM ptr, SCM port)
1394 {
1395 scm_puts_unlocked ("#<unknown-", port);
1396 scm_puts_unlocked (hdr, port);
1397 if (1) /* (scm_in_heap_p (ptr)) */ /* FIXME */
1398 {
1399 scm_puts_unlocked (" (0x", port);
1400 scm_uintprint (SCM_CELL_WORD_0 (ptr), 16, port);
1401 scm_puts_unlocked (" . 0x", port);
1402 scm_uintprint (SCM_CELL_WORD_1 (ptr), 16, port);
1403 scm_puts_unlocked (") @", port);
1404 }
1405 scm_puts_unlocked (" 0x", port);
1406 scm_uintprint (SCM_UNPACK (ptr), 16, port);
1407 scm_putc_unlocked ('>', port);
1408 }
1409
1410
1411 /* Print a list.
1412 */
1413 void
1414 scm_iprlist (char *hdr, SCM exp, int tlr, SCM port, scm_print_state *pstate)
1415 {
1416 register SCM hare, tortoise;
1417 long floor = pstate->top - 2;
1418 scm_puts_unlocked (hdr, port);
1419 /* CHECK_INTS; */
1420 if (pstate->fancyp)
1421 goto fancy_printing;
1422
1423 /* Run a hare and tortoise so that total time complexity will be
1424 O(depth * N) instead of O(N^2). */
1425 hare = SCM_CDR (exp);
1426 tortoise = exp;
1427 while (scm_is_pair (hare))
1428 {
1429 if (scm_is_eq (hare, tortoise))
1430 goto fancy_printing;
1431 hare = SCM_CDR (hare);
1432 if (!scm_is_pair (hare))
1433 break;
1434 hare = SCM_CDR (hare);
1435 tortoise = SCM_CDR (tortoise);
1436 }
1437
1438 /* No cdr cycles intrinsic to this list */
1439 scm_iprin1 (SCM_CAR (exp), port, pstate);
1440 for (exp = SCM_CDR (exp); scm_is_pair (exp); exp = SCM_CDR (exp))
1441 {
1442 register long i;
1443
1444 for (i = floor; i >= 0; --i)
1445 if (scm_is_eq (PSTATE_STACK_REF(pstate, i), exp))
1446 goto circref;
1447 PUSH_REF (pstate, exp);
1448 scm_putc_unlocked (' ', port);
1449 /* CHECK_INTS; */
1450 scm_iprin1 (SCM_CAR (exp), port, pstate);
1451 }
1452 if (!SCM_NULL_OR_NIL_P (exp))
1453 {
1454 scm_puts_unlocked (" . ", port);
1455 scm_iprin1 (exp, port, pstate);
1456 }
1457
1458 end:
1459 scm_putc_unlocked (tlr, port);
1460 pstate->top = floor + 2;
1461 return;
1462
1463 fancy_printing:
1464 {
1465 long n = pstate->length;
1466
1467 scm_iprin1 (SCM_CAR (exp), port, pstate);
1468 exp = SCM_CDR (exp); --n;
1469 for (; scm_is_pair (exp); exp = SCM_CDR (exp))
1470 {
1471 register unsigned long i;
1472
1473 for (i = 0; i < pstate->top; ++i)
1474 if (scm_is_eq (PSTATE_STACK_REF(pstate, i), exp))
1475 goto fancy_circref;
1476 if (pstate->fancyp)
1477 {
1478 if (n == 0)
1479 {
1480 scm_puts_unlocked (" ...", port);
1481 goto skip_tail;
1482 }
1483 else
1484 --n;
1485 }
1486 PUSH_REF(pstate, exp);
1487 ++pstate->list_offset;
1488 scm_putc_unlocked (' ', port);
1489 /* CHECK_INTS; */
1490 scm_iprin1 (SCM_CAR (exp), port, pstate);
1491 }
1492 }
1493 if (!SCM_NULL_OR_NIL_P (exp))
1494 {
1495 scm_puts_unlocked (" . ", port);
1496 scm_iprin1 (exp, port, pstate);
1497 }
1498 skip_tail:
1499 pstate->list_offset -= pstate->top - floor - 2;
1500 goto end;
1501
1502 fancy_circref:
1503 pstate->list_offset -= pstate->top - floor - 2;
1504
1505 circref:
1506 scm_puts_unlocked (" . ", port);
1507 print_circref (port, pstate, exp);
1508 goto end;
1509 }
1510
1511 \f
1512
1513 int
1514 scm_valid_oport_value_p (SCM val)
1515 {
1516 return (SCM_OPOUTPORTP (val)
1517 || (SCM_PORT_WITH_PS_P (val)
1518 && SCM_OPOUTPORTP (SCM_PORT_WITH_PS_PORT (val))));
1519 }
1520
1521 /* SCM_GPROC(s_write, "write", 1, 1, 0, scm_write, g_write); */
1522
1523 SCM
1524 scm_write (SCM obj, SCM port)
1525 {
1526 if (SCM_UNBNDP (port))
1527 port = scm_current_output_port ();
1528
1529 SCM_ASSERT (scm_valid_oport_value_p (port), port, SCM_ARG2, s_write);
1530
1531 scm_dynwind_begin (0);
1532 scm_dynwind_lock_port (SCM_COERCE_OUTPORT (port));
1533 scm_prin1 (obj, port, 1);
1534 scm_dynwind_end ();
1535
1536 return SCM_UNSPECIFIED;
1537 }
1538
1539
1540 /* SCM_GPROC(s_display, "display", 1, 1, 0, scm_display, g_display); */
1541
1542 SCM
1543 scm_display (SCM obj, SCM port)
1544 {
1545 if (SCM_UNBNDP (port))
1546 port = scm_current_output_port ();
1547
1548 SCM_ASSERT (scm_valid_oport_value_p (port), port, SCM_ARG2, s_display);
1549
1550 scm_dynwind_begin (0);
1551 scm_dynwind_lock_port (SCM_COERCE_OUTPORT (port));
1552 scm_prin1 (obj, port, 0);
1553 scm_dynwind_end ();
1554
1555 return SCM_UNSPECIFIED;
1556 }
1557
1558
1559 SCM_DEFINE (scm_simple_format, "simple-format", 2, 0, 1,
1560 (SCM destination, SCM message, SCM args),
1561 "Write @var{message} to @var{destination}, defaulting to\n"
1562 "the current output port.\n"
1563 "@var{message} can contain @code{~A} (was @code{%s}) and\n"
1564 "@code{~S} (was @code{%S}) escapes. When printed,\n"
1565 "the escapes are replaced with corresponding members of\n"
1566 "@var{args}:\n"
1567 "@code{~A} formats using @code{display} and @code{~S} formats\n"
1568 "using @code{write}.\n"
1569 "If @var{destination} is @code{#t}, then use the current output\n"
1570 "port, if @var{destination} is @code{#f}, then return a string\n"
1571 "containing the formatted text. Does not add a trailing newline.")
1572 #define FUNC_NAME s_scm_simple_format
1573 {
1574 SCM port, answer = SCM_UNSPECIFIED;
1575 int fReturnString = 0;
1576 int writingp;
1577 size_t start, p, end;
1578
1579 if (scm_is_eq (destination, SCM_BOOL_T))
1580 {
1581 destination = port = scm_current_output_port ();
1582 SCM_VALIDATE_OPORT_VALUE (1, destination);
1583 }
1584 else if (scm_is_false (destination))
1585 {
1586 fReturnString = 1;
1587 port = scm_mkstrport (SCM_INUM0, SCM_BOOL_F,
1588 SCM_OPN | SCM_WRTNG,
1589 FUNC_NAME);
1590 destination = port;
1591 }
1592 else
1593 {
1594 SCM_VALIDATE_OPORT_VALUE (1, destination);
1595 port = SCM_COERCE_OUTPORT (destination);
1596 }
1597 SCM_VALIDATE_STRING (2, message);
1598 SCM_VALIDATE_REST_ARGUMENT (args);
1599
1600 p = 0;
1601 start = 0;
1602 end = scm_i_string_length (message);
1603 for (p = start; p != end; ++p)
1604 if (scm_i_string_ref (message, p) == '~')
1605 {
1606 if (++p == end)
1607 break;
1608
1609 switch (scm_i_string_ref (message, p))
1610 {
1611 case 'A': case 'a':
1612 writingp = 0;
1613 break;
1614 case 'S': case 's':
1615 writingp = 1;
1616 break;
1617 case '~':
1618 scm_lfwrite_substr (message, start, p, port);
1619 start = p + 1;
1620 continue;
1621 case '%':
1622 scm_lfwrite_substr (message, start, p - 1, port);
1623 scm_newline (port);
1624 start = p + 1;
1625 continue;
1626 default:
1627 SCM_MISC_ERROR ("FORMAT: Unsupported format option ~~~A - use (ice-9 format) instead",
1628 scm_list_1 (SCM_MAKE_CHAR (scm_i_string_ref (message, p))));
1629
1630 }
1631
1632
1633 if (!scm_is_pair (args))
1634 SCM_MISC_ERROR ("FORMAT: Missing argument for ~~~A",
1635 scm_list_1 (SCM_MAKE_CHAR (scm_i_string_ref (message, p))));
1636
1637 scm_lfwrite_substr (message, start, p - 1, port);
1638 /* we pass destination here */
1639 scm_prin1 (SCM_CAR (args), destination, writingp);
1640 args = SCM_CDR (args);
1641 start = p + 1;
1642 }
1643
1644 scm_lfwrite_substr (message, start, p, port);
1645 if (!scm_is_eq (args, SCM_EOL))
1646 SCM_MISC_ERROR ("FORMAT: ~A superfluous arguments",
1647 scm_list_1 (scm_length (args)));
1648
1649 if (fReturnString)
1650 answer = scm_strport_to_string (destination);
1651
1652 return scm_return_first (answer, message);
1653 }
1654 #undef FUNC_NAME
1655
1656
1657 SCM_DEFINE (scm_newline, "newline", 0, 1, 0,
1658 (SCM port),
1659 "Send a newline to @var{port}.\n"
1660 "If @var{port} is omitted, send to the current output port.")
1661 #define FUNC_NAME s_scm_newline
1662 {
1663 if (SCM_UNBNDP (port))
1664 port = scm_current_output_port ();
1665
1666 SCM_VALIDATE_OPORT_VALUE (1, port);
1667
1668 scm_putc_unlocked ('\n', SCM_COERCE_OUTPORT (port));
1669 return SCM_UNSPECIFIED;
1670 }
1671 #undef FUNC_NAME
1672
1673 SCM_DEFINE (scm_write_char, "write-char", 1, 1, 0,
1674 (SCM chr, SCM port),
1675 "Send character @var{chr} to @var{port}.")
1676 #define FUNC_NAME s_scm_write_char
1677 {
1678 if (SCM_UNBNDP (port))
1679 port = scm_current_output_port ();
1680
1681 SCM_VALIDATE_CHAR (1, chr);
1682 SCM_VALIDATE_OPORT_VALUE (2, port);
1683
1684 port = SCM_COERCE_OUTPORT (port);
1685 if (!display_character (SCM_CHAR (chr), port,
1686 PORT_CONVERSION_HANDLER (port)))
1687 scm_encoding_error (__func__, errno,
1688 "cannot convert to output locale",
1689 port, chr);
1690
1691 return SCM_UNSPECIFIED;
1692 }
1693 #undef FUNC_NAME
1694
1695 \f
1696
1697 /* Call back to Scheme code to do the printing of special objects
1698 * (like structs). SCM_PRINTER_APPLY applies PROC to EXP and a smob
1699 * containing PORT and PSTATE. This object can be used as the port for
1700 * display/write etc to continue the current print chain. The REVEALED
1701 * field of PSTATE is set to true to indicate that the print state has
1702 * escaped to Scheme and thus has to be freed by the GC.
1703 */
1704
1705 scm_t_bits scm_tc16_port_with_ps;
1706
1707 /* Print exactly as the port itself would */
1708
1709 static int
1710 port_with_ps_print (SCM obj, SCM port, scm_print_state *pstate)
1711 {
1712 obj = SCM_PORT_WITH_PS_PORT (obj);
1713 return SCM_PORT_DESCRIPTOR (obj)->print (obj, port, pstate);
1714 }
1715
1716 SCM
1717 scm_printer_apply (SCM proc, SCM exp, SCM port, scm_print_state *pstate)
1718 {
1719 pstate->revealed = 1;
1720 return scm_call_2 (proc, exp,
1721 scm_i_port_with_print_state (port, pstate->handle));
1722 }
1723
1724 SCM_DEFINE (scm_port_with_print_state, "port-with-print-state", 1, 1, 0,
1725 (SCM port, SCM pstate),
1726 "Create a new port which behaves like @var{port}, but with an\n"
1727 "included print state @var{pstate}. @var{pstate} is optional.\n"
1728 "If @var{pstate} isn't supplied and @var{port} already has\n"
1729 "a print state, the old print state is reused.")
1730 #define FUNC_NAME s_scm_port_with_print_state
1731 {
1732 SCM_VALIDATE_OPORT_VALUE (1, port);
1733 if (!SCM_UNBNDP (pstate))
1734 SCM_VALIDATE_PRINTSTATE (2, pstate);
1735 return scm_i_port_with_print_state (port, pstate);
1736 }
1737 #undef FUNC_NAME
1738
1739 SCM_DEFINE (scm_get_print_state, "get-print-state", 1, 0, 0,
1740 (SCM port),
1741 "Return the print state of the port @var{port}. If @var{port}\n"
1742 "has no associated print state, @code{#f} is returned.")
1743 #define FUNC_NAME s_scm_get_print_state
1744 {
1745 if (SCM_PORT_WITH_PS_P (port))
1746 return SCM_PORT_WITH_PS_PS (port);
1747 if (SCM_OUTPUT_PORT_P (port))
1748 return SCM_BOOL_F;
1749 SCM_WRONG_TYPE_ARG (1, port);
1750 }
1751 #undef FUNC_NAME
1752
1753 \f
1754
1755 void
1756 scm_init_print ()
1757 {
1758 SCM type;
1759
1760 type = scm_make_vtable (scm_from_locale_string (SCM_PRINT_STATE_LAYOUT),
1761 SCM_BOOL_F);
1762 scm_set_struct_vtable_name_x (type, scm_from_latin1_symbol ("print-state"));
1763 scm_print_state_vtable = type;
1764
1765 /* Don't want to bind a wrapper class in GOOPS, so pass 0 as arg1. */
1766 scm_tc16_port_with_ps = scm_make_smob_type (0, 0);
1767 scm_set_smob_print (scm_tc16_port_with_ps, port_with_ps_print);
1768
1769 #include "libguile/print.x"
1770
1771 scm_init_opts (scm_print_options, scm_print_opts);
1772 scm_print_opts[SCM_PRINT_HIGHLIGHT_PREFIX_I].val =
1773 SCM_UNPACK (scm_from_locale_string ("{"));
1774 scm_print_opts[SCM_PRINT_HIGHLIGHT_SUFFIX_I].val =
1775 SCM_UNPACK (scm_from_locale_string ("}"));
1776 scm_print_opts[SCM_PRINT_KEYWORD_STYLE_I].val = SCM_UNPACK (sym_reader);
1777 }
1778
1779 /*
1780 Local Variables:
1781 c-file-style: "gnu"
1782 End:
1783 */