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