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