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