Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / libguile / print.c
CommitLineData
f4bc4e59 1/* Copyright (C) 1995-1999, 2000, 2001, 2002, 2003, 2004, 2006, 2008,
88c0a1d5 2 * 2009, 2010, 2011, 2012 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
MD
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"
327967ef 51#include "libguile/numbers.h"
6f3b0cc2 52#include "libguile/vm.h"
a0599745
MD
53
54#include "libguile/validate.h"
55#include "libguile/print.h"
22fc179a
HWN
56
57#include "libguile/private-options.h"
58
0f2d19dd
JB
59\f
60
07f49ac7
LC
61/* Character printers. */
62
478848cb
LC
63#define PORT_CONVERSION_HANDLER(port) \
64 SCM_PTAB_ENTRY (port)->ilseq_handler
65
f4bc4e59
LC
66static size_t display_string (const void *, int, size_t, SCM,
67 scm_t_string_failed_conversion_handler);
68
07f49ac7
LC
69static int display_character (scm_t_wchar, SCM,
70 scm_t_string_failed_conversion_handler);
f4bc4e59 71
07f49ac7
LC
72static void write_character (scm_t_wchar, SCM, int);
73
f4bc4e59
LC
74static void write_character_escaped (scm_t_wchar, int, SCM);
75
07f49ac7
LC
76\f
77
0f2d19dd
JB
78/* {Names of immediate symbols}
79 *
80 * This table must agree with the declarations in scm.h: {Immediate Symbols}.
81 */
82
e17d318f
DH
83/* This table must agree with the list of flags in tags.h. */
84static const char *iflagnames[] =
85{
86 "#f",
45f4cbdf
MW
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 "()",
e17d318f 90 "#t",
f60c2c4e
MW
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>",
45f4cbdf 94 "#<unspecified>",
e17d318f
DH
95 "#<undefined>",
96 "#<eof>",
e17d318f
DH
97
98 /* Unbound slot marker for GOOPS. For internal use in GOOPS only. */
99 "#<unbound>",
e17d318f
DH
100};
101
475fa9a5
MV
102SCM_SYMBOL (sym_reader, "reader");
103
92c2555f 104scm_t_option scm_print_opts[] = {
210c0325 105 { SCM_OPTION_SCM, "highlight-prefix", (scm_t_bits)SCM_BOOL_F_BITS,
81ae25da 106 "The string to print before highlighted values." },
210c0325 107 { SCM_OPTION_SCM, "highlight-suffix", (scm_t_bits)SCM_BOOL_F_BITS,
475fa9a5 108 "The string to print after highlighted values." },
210c0325 109 { SCM_OPTION_SCM, "quote-keywordish-symbols", (scm_t_bits)SCM_BOOL_F_BITS,
475fa9a5
MV
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; "
8500b186
AW
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'." },
62560650 115 { 0 },
e6e4c9af
MD
116};
117
a1ec6916 118SCM_DEFINE (scm_print_options, "print-options-interface", 0, 1, 0,
1bbd0b84 119 (SCM setting),
71331188 120 "Option interface for the print options. Instead of using\n"
1dd05fd8
MG
121 "this procedure directly, use the procedures\n"
122 "@code{print-enable}, @code{print-disable}, @code{print-set!}\n"
123 "and @code{print-options}.")
1bbd0b84 124#define FUNC_NAME s_scm_print_options
e6e4c9af 125{
a51ea417 126 SCM ans = scm_options (setting,
b7ff98dd 127 scm_print_opts,
1bbd0b84 128 FUNC_NAME);
e6e4c9af
MD
129 return ans;
130}
1bbd0b84 131#undef FUNC_NAME
e6e4c9af 132
0f2d19dd
JB
133\f
134/* {Printing of Scheme Objects}
135 */
136
a51ea417 137/* Detection of circular references.
c62fbfe1
MD
138 *
139 * Due to other constraints in the implementation, this code has bad
5d46ebe3
MD
140 * time complexity (O (depth * N)), The printer code can be
141 * rewritten to be O(N).
a51ea417 142 */
dbb5de29
NJ
143#define PUSH_REF(pstate, obj) \
144do \
145{ \
146 PSTATE_STACK_SET (pstate, pstate->top, obj); \
147 pstate->top++; \
148 if (pstate->top == pstate->ceiling) \
149 grow_ref_stack (pstate); \
1bbd0b84 150} while(0)
a51ea417 151
dbb5de29
NJ
152#define ENTER_NESTED_DATA(pstate, obj, label) \
153do \
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 { \
0607ebbf 163 scm_putc_unlocked ('#', port); \
dbb5de29
NJ
164 return; \
165 } \
166 } \
167 PUSH_REF(pstate, obj); \
1bbd0b84 168} while(0)
a51ea417 169
dbb5de29
NJ
170#define EXIT_NESTED_DATA(pstate) \
171do \
172{ \
173 --pstate->top; \
174 PSTATE_STACK_SET (pstate, pstate->top, SCM_UNDEFINED); \
175} \
176while (0)
c62fbfe1 177
d5cf5324
DH
178SCM scm_print_state_vtable = SCM_BOOL_F;
179static SCM print_state_pool = SCM_EOL;
9de87eea 180scm_i_pthread_mutex_t print_state_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
c4f37e80 181
f843a84c 182#ifdef GUILE_DEBUG /* Used for debugging purposes */
1cc91f1b 183
3b3b36dd 184SCM_DEFINE (scm_current_pstate, "current-pstate", 0, 0, 0,
1bbd0b84 185 (),
d5cf5324 186 "Return the current-pstate -- the car of the\n"
5352393c
MG
187 "@code{print_state_pool}. @code{current-pstate} is only\n"
188 "included in @code{--enable-guile-debug} builds.")
1bbd0b84 189#define FUNC_NAME s_scm_current_pstate
c62fbfe1 190{
d2e53ed6 191 if (!scm_is_null (print_state_pool))
d5cf5324 192 return SCM_CAR (print_state_pool);
a0adfbf0 193 else
0a284a4e 194 return SCM_BOOL_F;
c62fbfe1 195}
1bbd0b84
GB
196#undef FUNC_NAME
197
c62fbfe1
MD
198#endif
199
200#define PSTATE_SIZE 50L
201
698c0295 202static SCM
1bbd0b84 203make_print_state (void)
698c0295 204{
d5cf5324
DH
205 SCM print_state
206 = scm_make_struct (scm_print_state_vtable, SCM_INUM0, SCM_EOL);
bf685b6d 207 scm_print_state *pstate = SCM_PRINT_STATE (print_state);
00ffa0e7 208 pstate->ref_vect = scm_c_make_vector (PSTATE_SIZE, SCM_UNDEFINED);
4057a3e0 209 pstate->ceiling = SCM_SIMPLE_VECTOR_LENGTH (pstate->ref_vect);
d232520a 210 pstate->highlight_objects = SCM_EOL;
698c0295
MD
211 return print_state;
212}
1cc91f1b 213
c62fbfe1
MD
214SCM
215scm_make_print_state ()
c62fbfe1 216{
230d095f 217 SCM answer = SCM_BOOL_F;
698c0295
MD
218
219 /* First try to allocate a print state from the pool */
9de87eea 220 scm_i_pthread_mutex_lock (&print_state_mutex);
d2e53ed6 221 if (!scm_is_null (print_state_pool))
698c0295 222 {
d5cf5324
DH
223 answer = SCM_CAR (print_state_pool);
224 print_state_pool = SCM_CDR (print_state_pool);
698c0295 225 }
9de87eea 226 scm_i_pthread_mutex_unlock (&print_state_mutex);
698c0295 227
7888309b 228 return scm_is_false (answer) ? make_print_state () : answer;
c62fbfe1 229}
a51ea417 230
698c0295 231void
6e8d25a6 232scm_free_print_state (SCM print_state)
698c0295
MD
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;
bb35f315 242 pstate->revealed = 0;
d232520a 243 pstate->highlight_objects = SCM_EOL;
9de87eea 244 scm_i_pthread_mutex_lock (&print_state_mutex);
16d4699b 245 handle = scm_cons (print_state, print_state_pool);
d5cf5324 246 print_state_pool = handle;
9de87eea 247 scm_i_pthread_mutex_unlock (&print_state_mutex);
dfd03fb9
MD
248}
249
250SCM
251scm_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)));
698c0295 265}
1cc91f1b 266
a51ea417 267static void
1bbd0b84 268grow_ref_stack (scm_print_state *pstate)
a51ea417 269{
4057a3e0
MV
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;
00ffa0e7 273 SCM new_vect = scm_c_make_vector (new_size, SCM_UNDEFINED);
b17004b8
DH
274 unsigned long int i;
275
276 for (i = 0; i != old_size; ++i)
4057a3e0 277 SCM_SIMPLE_VECTOR_SET (new_vect, i, SCM_SIMPLE_VECTOR_REF (old_vect, i));
b17004b8
DH
278
279 pstate->ref_vect = new_vect;
bf685b6d 280 pstate->ceiling = new_size;
a51ea417
MD
281}
282
509759dd
MV
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))
1cc91f1b 285
a51ea417 286static void
34d19ef6 287print_circref (SCM port, scm_print_state *pstate, SCM ref)
a51ea417 288{
c014a02e
ML
289 register long i;
290 long self = pstate->top - 1;
c62fbfe1 291 i = pstate->top - 1;
509759dd 292 if (scm_is_pair (PSTATE_STACK_REF (pstate, i)))
c62fbfe1
MD
293 {
294 while (i > 0)
295 {
509759dd
MV
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))))
c62fbfe1
MD
299 break;
300 --i;
301 }
302 self = i;
303 }
304 for (i = pstate->top - 1; 1; --i)
509759dd 305 if (scm_is_eq (PSTATE_STACK_REF(pstate, i), ref))
c62fbfe1 306 break;
0607ebbf 307 scm_putc_unlocked ('#', port);
c62fbfe1 308 scm_intprint (i - self, 10, port);
0607ebbf 309 scm_putc_unlocked ('#', port);
a51ea417
MD
310}
311
6662998f
MV
312/* Print the name of a symbol. */
313
475fa9a5 314static int
15671c6e 315quote_keywordish_symbols (void)
475fa9a5 316{
15671c6e 317 SCM option = SCM_PRINT_KEYWORD_STYLE;
475fa9a5 318
475fa9a5
MV
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
2e9fc9fc
AW
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
4164dd6d 338/* FIXME: Cache this information on the symbol, somehow. */
15671c6e
AW
339static int
340symbol_has_extended_read_syntax (SCM sym)
6662998f 341{
15671c6e
AW
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
4164dd6d
AW
351 switch (c)
352 {
353 case '\'':
354 case '`':
355 case ',':
356 case '"':
357 case ';':
358 case '#':
359 /* Some initial-character constraints. */
360 return 1;
15671c6e 361
4164dd6d
AW
362 case ':':
363 /* Symbols that look like keywords. */
364 return quote_keywordish_symbols ();
15671c6e 365
4164dd6d
AW
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 }
15671c6e 392
2e9fc9fc
AW
393 /* Other disallowed first characters. */
394 if (!uc_is_general_category_withtable (c, INITIAL_IDENTIFIER_MASK))
395 return 1;
396
4164dd6d
AW
397 /* Keywords can be identified by trailing colons too. */
398 if (scm_i_symbol_ref (sym, len - 1) == ':')
399 return quote_keywordish_symbols ();
400
2e9fc9fc
AW
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++)
6662998f 405 {
2e9fc9fc
AW
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;
6662998f 411 }
c6b49e89 412
15671c6e
AW
413 return 0;
414}
415
416static void
417print_normal_symbol (SCM sym, SCM port)
418{
4164dd6d
AW
419 size_t len;
420 scm_t_string_failed_conversion_handler strategy;
421
422 len = scm_i_symbol_length (sym);
0dd7c540 423 strategy = SCM_PTAB_ENTRY (port)->ilseq_handler;
4164dd6d
AW
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);
15671c6e
AW
429}
430
15671c6e
AW
431static void
432print_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);
478848cb 438 strategy = PORT_CONVERSION_HANDLER (port);
15671c6e 439
f209aeee 440 scm_lfwrite_unlocked ("#{", 2, port);
15671c6e
AW
441
442 for (pos = 0; pos < len; pos++)
443 {
444 scm_t_wchar c = scm_i_symbol_ref (sym, pos);
445
2e9fc9fc
AW
446 if (uc_is_general_category_withtable (c,
447 SUBSEQUENT_IDENTIFIER_MASK
448 | UC_CATEGORY_MASK_Zs))
15671c6e 449 {
15671c6e
AW
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));
2e9fc9fc
AW
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);
15671c6e
AW
460 }
461 }
462
f209aeee 463 scm_lfwrite_unlocked ("}#", 2, port);
15671c6e
AW
464}
465
466/* FIXME: allow R6RS hex escapes instead of #{...}#. */
4164dd6d
AW
467static void
468print_symbol (SCM sym, SCM port)
15671c6e
AW
469{
470 if (symbol_has_extended_read_syntax (sym))
471 print_extended_symbol (sym, port);
472 else
473 print_normal_symbol (sym, port);
6662998f
MV
474}
475
e23106d5
MG
476void
477scm_print_symbol_name (const char *str, size_t len, SCM port)
478{
25d50a05 479 SCM symbol = scm_from_utf8_symboln (str, len);
4164dd6d 480 print_symbol (symbol, port);
e23106d5
MG
481}
482
c62fbfe1 483/* Print generally. Handles both write and display according to PSTATE.
0f2d19dd 484 */
8b840115
MD
485SCM_GPROC(s_write, "write", 1, 1, 0, scm_write, g_write);
486SCM_GPROC(s_display, "display", 1, 1, 0, scm_display, g_display);
1cc91f1b 487
d232520a
MV
488static void iprin1 (SCM exp, SCM port, scm_print_state *pstate);
489
dea901d6
MG
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 { \
0607ebbf 499 scm_puts_unlocked ("x", port); \
dea901d6
MG
500 scm_intprint (i, 16, port); \
501 } \
502 } \
503 while (0)
504
505
0f2d19dd 506void
1bbd0b84 507scm_iprin1 (SCM exp, SCM port, scm_print_state *pstate)
d232520a
MV
508{
509 if (pstate->fancyp
510 && scm_is_true (scm_memq (exp, pstate->highlight_objects)))
511 {
81ae25da 512 scm_display (SCM_PRINT_HIGHLIGHT_PREFIX, port);
d232520a 513 iprin1 (exp, port, pstate);
81ae25da 514 scm_display (SCM_PRINT_HIGHLIGHT_SUFFIX, port);
d232520a
MV
515 }
516 else
517 iprin1 (exp, port, pstate);
518}
519
520static void
521iprin1 (SCM exp, SCM port, scm_print_state *pstate)
0f2d19dd 522{
54778cd3 523 switch (SCM_ITAG3 (exp))
0f2d19dd 524 {
e34f941a
DH
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:
e11e83f3 535 scm_intprint (SCM_I_INUM (exp), 10, port);
0f2d19dd 536 break;
e34f941a 537 case scm_tc3_imm24:
7866a09b 538 if (SCM_CHARP (exp))
0f2d19dd 539 {
b7f3516f 540 if (SCM_WRITINGP (pstate))
07f49ac7
LC
541 write_character (SCM_CHAR (exp), port, 0);
542 else
b7f3516f 543 {
07f49ac7 544 if (!display_character (SCM_CHAR (exp), port,
478848cb 545 PORT_CONVERSION_HANDLER (port)))
07f49ac7
LC
546 scm_encoding_error (__func__, errno,
547 "cannot convert to output locale",
6851d3be 548 port, exp);
b7f3516f 549 }
0f2d19dd 550 }
a51ea417 551 else if (SCM_IFLAGP (exp)
e17d318f
DH
552 && ((size_t) SCM_IFLAGNUM (exp) < (sizeof iflagnames / sizeof (char *))))
553 {
0607ebbf 554 scm_puts_unlocked (iflagnames [SCM_IFLAGNUM (exp)], port);
e17d318f 555 }
0f2d19dd 556 else
e34f941a
DH
557 {
558 /* unknown immediate value */
559 scm_ipruk ("immediate", exp, port);
560 }
0f2d19dd 561 break;
e34f941a 562 case scm_tc3_cons:
0f2d19dd
JB
563 switch (SCM_TYP7 (exp))
564 {
904a077d
MV
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;
b2b33168 571 if (SCM_UNPACK (print) == 0)
904a077d 572 goto print_struct;
dfd03fb9 573 pwps = scm_i_port_with_print_state (port, pstate->handle);
7663c008 574 pstate->revealed = 1;
fa075d40 575 scm_call_2 (print, exp, pwps);
904a077d
MV
576 }
577 else
578 {
579 print_struct:
580 scm_print_struct (exp, port, pstate);
581 }
582 EXIT_NESTED_DATA (pstate);
583 }
584 break;
0f2d19dd
JB
585 case scm_tcs_cons_imcar:
586 case scm_tcs_cons_nimcar:
c62fbfe1
MD
587 ENTER_NESTED_DATA (pstate, exp, circref);
588 scm_iprlist ("(", exp, ')', port, pstate);
589 EXIT_NESTED_DATA (pstate);
a51ea417
MD
590 break;
591 circref:
c62fbfe1 592 print_circref (port, pstate, exp);
0f2d19dd 593 break;
534c55a9
DH
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;
f92e85f7
MV
605 case scm_tc16_fraction:
606 scm_i_print_fraction (exp, port, pstate);
607 break;
534c55a9
DH
608 }
609 break;
9c44cd45
MG
610 case scm_tc7_string:
611 if (SCM_WRITINGP (pstate))
612 {
07f49ac7 613 size_t len, i;
9c44cd45 614
f4bc4e59 615 display_character ('"', port, iconveh_question_mark);
9c44cd45
MG
616 len = scm_i_string_length (exp);
617 for (i = 0; i < len; ++i)
07f49ac7
LC
618 write_character (scm_i_string_ref (exp, i), port, 1);
619
f4bc4e59 620 display_character ('"', port, iconveh_question_mark);
9c44cd45
MG
621 scm_remember_upto_here_1 (exp);
622 }
623 else
f4bc4e59
LC
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,
478848cb 631 PORT_CONVERSION_HANDLER (port));
f4bc4e59 632 if (SCM_UNLIKELY (printed < len))
f4bc4e59
LC
633 scm_encoding_error (__func__, errno,
634 "cannot convert to output locale",
6851d3be 635 port, scm_c_string_ref (exp, printed));
f4bc4e59
LC
636 }
637
9c44cd45
MG
638 scm_remember_upto_here_1 (exp);
639 break;
28b06554 640 case scm_tc7_symbol:
cc95e00a 641 if (scm_i_symbol_is_interned (exp))
9ff28a13 642 {
4164dd6d 643 print_symbol (exp, port);
9ff28a13
MV
644 scm_remember_upto_here_1 (exp);
645 }
646 else
647 {
0607ebbf 648 scm_puts_unlocked ("#<uninterned-symbol ", port);
4164dd6d 649 print_symbol (exp, port);
0607ebbf 650 scm_putc_unlocked (' ', port);
0345e278 651 scm_uintprint (SCM_UNPACK (exp), 16, port);
0607ebbf 652 scm_putc_unlocked ('>', port);
9ff28a13 653 }
6662998f 654 break;
e5aca4b5
MV
655 case scm_tc7_variable:
656 scm_i_variable_print (exp, port, pstate);
657 break;
2fb924f6
AW
658 case scm_tc7_program:
659 scm_i_program_print (exp, port, pstate);
660 break;
5b46a8c2
LC
661 case scm_tc7_pointer:
662 scm_i_pointer_print (exp, port, pstate);
e2c2a699 663 break;
c99de5aa
AW
664 case scm_tc7_hashtable:
665 scm_i_hashtable_print (exp, port, pstate);
666 break;
26b26354
AW
667 case scm_tc7_weak_set:
668 scm_i_weak_set_print (exp, port, pstate);
669 break;
7005c60f
AW
670 case scm_tc7_weak_table:
671 scm_i_weak_table_print (exp, port, pstate);
672 break;
9ea31741
AW
673 case scm_tc7_fluid:
674 scm_i_fluid_print (exp, port, pstate);
675 break;
45cf2428
AW
676 case scm_tc7_dynamic_state:
677 scm_i_dynamic_state_print (exp, port, pstate);
678 break;
6f3b0cc2
AW
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;
b2637c98 691 case scm_tc7_array:
c62fbfe1 692 ENTER_NESTED_DATA (pstate, exp, circref);
b2637c98 693 scm_i_print_array (exp, port, pstate);
88c0a1d5 694 EXIT_NESTED_DATA (pstate);
b2637c98 695 break;
807e5a66
LC
696 case scm_tc7_bytevector:
697 scm_i_print_bytevector (exp, port, pstate);
698 break;
ff1feca9
AW
699 case scm_tc7_bitvector:
700 scm_i_print_bitvector (exp, port, pstate);
701 break;
0f2d19dd 702 case scm_tc7_wvect:
c62fbfe1 703 ENTER_NESTED_DATA (pstate, exp, circref);
91ee7515 704 scm_puts_unlocked ("#w(", port);
0f2d19dd 705 goto common_vector_printer;
0f2d19dd 706 case scm_tc7_vector:
c62fbfe1 707 ENTER_NESTED_DATA (pstate, exp, circref);
0607ebbf 708 scm_puts_unlocked ("#(", port);
0f2d19dd 709 common_vector_printer:
9fbaf27c 710 {
c014a02e 711 register long i;
4057a3e0 712 long last = SCM_SIMPLE_VECTOR_LENGTH (exp) - 1;
9fbaf27c 713 int cutp = 0;
4057a3e0
MV
714 if (pstate->fancyp
715 && SCM_SIMPLE_VECTOR_LENGTH (exp) > pstate->length)
9fbaf27c
MD
716 {
717 last = pstate->length - 1;
718 cutp = 1;
719 }
a141db86
AW
720 for (i = 0; i < last; ++i)
721 {
722 scm_iprin1 (scm_c_vector_ref (exp, i), port, pstate);
0607ebbf 723 scm_putc_unlocked (' ', port);
a141db86 724 }
9fbaf27c
MD
725 if (i == last)
726 {
727 /* CHECK_INTS; */
c367c4b4 728 scm_iprin1 (scm_c_vector_ref (exp, i), port, pstate);
9fbaf27c
MD
729 }
730 if (cutp)
0607ebbf
AW
731 scm_puts_unlocked (" ...", port);
732 scm_putc_unlocked (')', port);
9fbaf27c 733 }
c62fbfe1 734 EXIT_NESTED_DATA (pstate);
0f2d19dd 735 break;
0f2d19dd 736 case scm_tc7_port:
5ca6dc39 737 {
62bd5d66
AW
738 scm_t_ptob_descriptor *ptob = SCM_PORT_DESCRIPTOR (exp);
739 if (ptob->print && ptob->print (exp, port, pstate))
a51ea417 740 break;
5ca6dc39
JB
741 goto punk;
742 }
743 case scm_tc7_smob:
7a7f7c53
DH
744 ENTER_NESTED_DATA (pstate, exp, circref);
745 SCM_SMOB_DESCRIPTOR (exp).print (exp, port, pstate);
746 EXIT_NESTED_DATA (pstate);
747 break;
0f2d19dd 748 default:
314b8716 749 /* case scm_tcs_closures: */
a51ea417
MD
750 punk:
751 scm_ipruk ("type", exp, port);
0f2d19dd
JB
752 }
753 }
754}
755
c62fbfe1
MD
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 */
1cc91f1b 760
bb35f315
MV
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
a51ea417 765void
1bbd0b84 766scm_prin1 (SCM exp, SCM port, int writingp)
a51ea417 767{
c4f37e80
MV
768 SCM handle = SCM_BOOL_F; /* Will GC protect the handle whilst unlinked */
769 SCM pstate_scm;
c62fbfe1 770 scm_print_state *pstate;
15635be5 771 int old_writingp;
c62fbfe1 772
bb35f315
MV
773 /* If PORT is a print-state/port pair, use that. Else create a new
774 print-state. */
c4f37e80 775
0c95b57d 776 if (SCM_PORT_WITH_PS_P (port))
bb35f315 777 {
52235e71
MD
778 pstate_scm = SCM_PORT_WITH_PS_PS (port);
779 port = SCM_PORT_WITH_PS_PORT (port);
bb35f315
MV
780 }
781 else
c62fbfe1 782 {
c4f37e80 783 /* First try to allocate a print state from the pool */
9de87eea 784 scm_i_pthread_mutex_lock (&print_state_mutex);
d2e53ed6 785 if (!scm_is_null (print_state_pool))
c4f37e80 786 {
d5cf5324
DH
787 handle = print_state_pool;
788 print_state_pool = SCM_CDR (print_state_pool);
c4f37e80 789 }
9de87eea 790 scm_i_pthread_mutex_unlock (&print_state_mutex);
7888309b 791 if (scm_is_false (handle))
d5cf5324 792 handle = scm_list_1 (make_print_state ());
c4f37e80 793 pstate_scm = SCM_CAR (handle);
c62fbfe1 794 }
c62fbfe1 795
c4f37e80 796 pstate = SCM_PRINT_STATE (pstate_scm);
15635be5 797 old_writingp = pstate->writingp;
c62fbfe1
MD
798 pstate->writingp = writingp;
799 scm_iprin1 (exp, port, pstate);
15635be5 800 pstate->writingp = old_writingp;
c62fbfe1 801
bb35f315
MV
802 /* Return print state to pool if it has been created above and
803 hasn't escaped to Scheme. */
804
7888309b 805 if (scm_is_true (handle) && !pstate->revealed)
c4f37e80 806 {
9de87eea 807 scm_i_pthread_mutex_lock (&print_state_mutex);
d5cf5324
DH
808 SCM_SETCDR (handle, print_state_pool);
809 print_state_pool = handle;
9de87eea 810 scm_i_pthread_mutex_unlock (&print_state_mutex);
c4f37e80 811 }
a51ea417
MD
812}
813
f4bc4e59
LC
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. */
816static size_t
817codepoint_to_utf8 (scm_t_wchar ch, scm_t_uint8 utf8[4])
9c44cd45 818{
f4bc4e59
LC
819 size_t len;
820 scm_t_uint32 codepoint;
821
822 codepoint = (scm_t_uint32) ch;
07f49ac7 823
f4bc4e59 824 if (codepoint <= 0x7f)
07f49ac7 825 {
f4bc4e59
LC
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);
07f49ac7
LC
841 }
842 else
843 {
f4bc4e59
LC
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
f4bc4e59
LC
854#define STR_REF(s, x) \
855 (narrow_p \
856 ? (scm_t_wchar) ((unsigned char *) (s))[x] \
857 : ((scm_t_wchar *) (s))[x])
858
7b292a9d
LC
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. */
861static size_t
862display_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. */
f209aeee 884 scm_lfwrite_unlocked (utf8_buf, utf8_len, port);
7b292a9d
LC
885 printed += i - printed;
886 }
887
888 assert (printed == len);
889
890 return len;
891}
892
79eb47ea
AW
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. */
895static size_t
896display_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
7b292a9d
LC
941/* Convert STR through PORT's output conversion descriptor and write the
942 output to PORT. Return the number of codepoints written. */
943static size_t
944display_string_using_iconv (const void *str, int narrow_p, size_t len,
945 SCM port,
946 scm_t_string_failed_conversion_handler strategy)
947{
f4bc4e59 948 size_t printed;
6c98257f 949 scm_t_iconv_descriptors *id;
f4bc4e59 950
6c98257f 951 id = scm_i_port_iconv_descriptors (port);
f4bc4e59 952
f4bc4e59
LC
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++)
07f49ac7 967 {
f4bc4e59
LC
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
6c98257f 980 done = iconv (id->output_cd, &input, &input_left,
f4bc4e59 981 &output, &output_left);
07f49ac7 982
f4bc4e59
LC
983 output_len = sizeof (encoded_output) - output_left;
984
985 if (SCM_UNLIKELY (done == (size_t) -1))
986 {
b2548e23
AW
987 int errno_save = errno;
988
f4bc4e59 989 /* Reset the `iconv' state. */
6c98257f 990 iconv (id->output_cd, NULL, NULL, NULL, NULL);
f4bc4e59 991
7174bc08 992 /* Print the OUTPUT_LEN bytes successfully converted. */
f209aeee 993 scm_lfwrite_unlocked (encoded_output, output_len, port);
7174bc08
LC
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
b2548e23 1000 if (errno_save == EILSEQ &&
f4bc4e59 1001 strategy != SCM_FAILED_CONVERSION_ERROR)
07f49ac7 1002 {
f4bc4e59
LC
1003 /* Conversion failed somewhere in INPUT and we want to
1004 escape or substitute the offending input character. */
1005
f4bc4e59 1006 if (strategy == SCM_FAILED_CONVERSION_ESCAPE_SEQUENCE)
f1ee6d54 1007 {
f4bc4e59
LC
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);
f1ee6d54 1013 }
07f49ac7 1014 else
f4bc4e59
LC
1015 /* STRATEGY is `SCM_FAILED_CONVERSION_QUESTION_MARK'. */
1016 display_string ("?", 1, 1, port, strategy);
9c44cd45 1017
f4bc4e59
LC
1018 printed++;
1019 }
1020 else
1021 /* Something bad happened that we can't handle: bail out. */
1022 break;
07f49ac7
LC
1023 }
1024 else
f4bc4e59
LC
1025 {
1026 /* INPUT was successfully converted, entirely; print the
1027 result. */
f209aeee 1028 scm_lfwrite_unlocked (encoded_output, output_len, port);
f4bc4e59
LC
1029 codepoints_read = i - printed;
1030 printed += codepoints_read;
1031 }
07f49ac7
LC
1032 }
1033
1034 return printed;
7b292a9d
LC
1035}
1036
f4bc4e59 1037#undef STR_REF
7b292a9d
LC
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'. */
1044static size_t
1045display_string (const void *str, int narrow_p,
1046 size_t len, SCM port,
1047 scm_t_string_failed_conversion_handler strategy)
7b292a9d
LC
1048{
1049 scm_t_port *pt;
1050
1051 pt = SCM_PTAB_ENTRY (port);
1052
6c98257f 1053 if (pt->encoding_mode == SCM_PORT_ENCODING_MODE_UTF8)
7b292a9d 1054 return display_string_as_utf8 (str, narrow_p, len, port);
79eb47ea
AW
1055 else if (pt->encoding_mode == SCM_PORT_ENCODING_MODE_LATIN1)
1056 return display_string_as_latin1 (str, narrow_p, len, port, strategy);
7b292a9d 1057 else
79eb47ea 1058 return display_string_using_iconv (str, narrow_p, len, port, strategy);
f4bc4e59
LC
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.) */
1064static int
1065display_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;
07f49ac7
LC
1069}
1070
33d92fe6
LC
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. */
1074static int
1075write_combining_character (scm_t_wchar ch, SCM port)
1076{
f4bc4e59
LC
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}
33d92fe6 1084
f4bc4e59
LC
1085/* Write CH to PORT in its escaped form, using the string escape syntax
1086 if STRING_ESCAPES_P is non-zero. */
1087static void
1088write_character_escaped (scm_t_wchar ch, int string_escapes_p, SCM port)
1089{
1090 if (string_escapes_p)
33d92fe6 1091 {
f4bc4e59
LC
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)
33d92fe6 1099 {
f4bc4e59
LC
1100 /* Use special escapes for some C0 controls. */
1101 buf[0] = '\\';
1102 buf[1] = escapes[ch - 0x07];
f209aeee 1103 scm_lfwrite_unlocked (buf, 2, port);
f4bc4e59
LC
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];
f209aeee 1113 scm_lfwrite_unlocked (buf, 4, port);
f4bc4e59
LC
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)];
f209aeee 1123 scm_lfwrite_unlocked (buf, 6, port);
f4bc4e59
LC
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)];
f209aeee 1135 scm_lfwrite_unlocked (buf, 8, port);
f4bc4e59 1136 }
33d92fe6
LC
1137 }
1138 else
f4bc4e59
LC
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] = '\\';
f209aeee 1158 scm_lfwrite_unlocked (buf + i, 9 - i, port);
f4bc4e59 1159 }
33d92fe6
LC
1160 }
1161 else
f4bc4e59
LC
1162 {
1163 /* Represent CH using the character escape syntax. */
1164 const char *name;
33d92fe6 1165
f4bc4e59
LC
1166 name = scm_i_charname (SCM_MAKE_CHAR (ch));
1167 if (name != NULL)
0607ebbf 1168 scm_puts_unlocked (name, port);
f4bc4e59
LC
1169 else
1170 PRINT_CHAR_ESCAPE (ch, port);
1171 }
33d92fe6
LC
1172}
1173
07f49ac7
LC
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. */
1178static void
1179write_character (scm_t_wchar ch, SCM port, int string_escapes_p)
1180{
1181 int printed = 0;
f4bc4e59
LC
1182 scm_t_string_failed_conversion_handler strategy;
1183
478848cb 1184 strategy = PORT_CONVERSION_HANDLER (port);
07f49ac7
LC
1185
1186 if (string_escapes_p)
1187 {
1188 /* Check if CH deserves special treatment. */
1189 if (ch == '"' || ch == '\\')
1190 {
f4bc4e59
LC
1191 display_character ('\\', port, iconveh_question_mark);
1192 display_character (ch, port, strategy);
07f49ac7
LC
1193 printed = 1;
1194 }
8500b186
AW
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 }
07f49ac7
LC
1201 else if (ch == ' ' || ch == '\n')
1202 {
f4bc4e59 1203 display_character (ch, port, strategy);
07f49ac7
LC
1204 printed = 1;
1205 }
1206 }
1207 else
33d92fe6 1208 {
f4bc4e59 1209 display_string ("#\\", 1, 2, port, iconveh_question_mark);
33d92fe6
LC
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 }
07f49ac7
LC
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)
f4bc4e59
LC
1228 /* CH isn't graphic or cannot be represented in PORT's encoding. */
1229 write_character_escaped (ch, string_escapes_p, port);
9c44cd45 1230}
0f2d19dd 1231
b908768a
LC
1232/* Display STR to PORT from START inclusive to END exclusive. */
1233void
1234scm_i_display_substring (SCM str, size_t start, size_t end, SCM port)
1235{
1236 int narrow_p;
1237 const char *buf;
1238 size_t len, printed;
1239
1240 buf = scm_i_string_data (str);
1241 len = end - start;
1242 narrow_p = scm_i_is_narrow_string (str);
1243 buf += start * (narrow_p ? sizeof (char) : sizeof (scm_t_wchar));
1244
1245 printed = display_string (buf, narrow_p, end - start, port,
1246 PORT_CONVERSION_HANDLER (port));
1247
1248 if (SCM_UNLIKELY (printed < len))
1249 scm_encoding_error (__func__, errno,
1250 "cannot convert to output locale",
1251 port, scm_c_string_ref (str, printed + start));
1252}
1253
1254\f
0f2d19dd
JB
1255/* Print an integer.
1256 */
1cc91f1b 1257
0f2d19dd 1258void
a406c9e9 1259scm_intprint (scm_t_intmax n, int radix, SCM port)
0f2d19dd
JB
1260{
1261 char num_buf[SCM_INTBUFLEN];
f209aeee 1262 scm_lfwrite_unlocked (num_buf, scm_iint2str (n, radix, num_buf), port);
0f2d19dd
JB
1263}
1264
a406c9e9
MV
1265void
1266scm_uintprint (scm_t_uintmax n, int radix, SCM port)
1267{
1268 char num_buf[SCM_INTBUFLEN];
f209aeee 1269 scm_lfwrite_unlocked (num_buf, scm_iuint2str (n, radix, num_buf), port);
a406c9e9
MV
1270}
1271
0f2d19dd
JB
1272/* Print an object of unrecognized type.
1273 */
1cc91f1b 1274
0f2d19dd 1275void
1bbd0b84 1276scm_ipruk (char *hdr, SCM ptr, SCM port)
0f2d19dd 1277{
0607ebbf
AW
1278 scm_puts_unlocked ("#<unknown-", port);
1279 scm_puts_unlocked (hdr, port);
26224b3f 1280 if (1) /* (scm_in_heap_p (ptr)) */ /* FIXME */
0f2d19dd 1281 {
0607ebbf 1282 scm_puts_unlocked (" (0x", port);
0345e278 1283 scm_uintprint (SCM_CELL_WORD_0 (ptr), 16, port);
0607ebbf 1284 scm_puts_unlocked (" . 0x", port);
0345e278 1285 scm_uintprint (SCM_CELL_WORD_1 (ptr), 16, port);
0607ebbf 1286 scm_puts_unlocked (") @", port);
0f2d19dd 1287 }
0607ebbf 1288 scm_puts_unlocked (" 0x", port);
0345e278 1289 scm_uintprint (SCM_UNPACK (ptr), 16, port);
0607ebbf 1290 scm_putc_unlocked ('>', port);
0f2d19dd
JB
1291}
1292
1cc91f1b 1293
904a077d 1294/* Print a list.
22a52da1 1295 */
0f2d19dd 1296void
34d19ef6 1297scm_iprlist (char *hdr, SCM exp, int tlr, SCM port, scm_print_state *pstate)
0f2d19dd 1298{
c62fbfe1 1299 register SCM hare, tortoise;
c014a02e 1300 long floor = pstate->top - 2;
0607ebbf 1301 scm_puts_unlocked (hdr, port);
0f2d19dd 1302 /* CHECK_INTS; */
c62fbfe1
MD
1303 if (pstate->fancyp)
1304 goto fancy_printing;
1305
1306 /* Run a hare and tortoise so that total time complexity will be
1307 O(depth * N) instead of O(N^2). */
1308 hare = SCM_CDR (exp);
1309 tortoise = exp;
d2e53ed6 1310 while (scm_is_pair (hare))
c62fbfe1 1311 {
bc36d050 1312 if (scm_is_eq (hare, tortoise))
c62fbfe1
MD
1313 goto fancy_printing;
1314 hare = SCM_CDR (hare);
d2e53ed6 1315 if (!scm_is_pair (hare))
c62fbfe1
MD
1316 break;
1317 hare = SCM_CDR (hare);
1318 tortoise = SCM_CDR (tortoise);
1319 }
1320
1321 /* No cdr cycles intrinsic to this list */
1322 scm_iprin1 (SCM_CAR (exp), port, pstate);
d2e53ed6 1323 for (exp = SCM_CDR (exp); scm_is_pair (exp); exp = SCM_CDR (exp))
0f2d19dd 1324 {
c014a02e 1325 register long i;
5ca6dc39 1326
c62fbfe1 1327 for (i = floor; i >= 0; --i)
509759dd 1328 if (scm_is_eq (PSTATE_STACK_REF(pstate, i), exp))
c62fbfe1
MD
1329 goto circref;
1330 PUSH_REF (pstate, exp);
0607ebbf 1331 scm_putc_unlocked (' ', port);
0f2d19dd 1332 /* CHECK_INTS; */
c62fbfe1 1333 scm_iprin1 (SCM_CAR (exp), port, pstate);
0f2d19dd 1334 }
c96d76b8 1335 if (!SCM_NULL_OR_NIL_P (exp))
0f2d19dd 1336 {
0607ebbf 1337 scm_puts_unlocked (" . ", port);
c62fbfe1 1338 scm_iprin1 (exp, port, pstate);
0f2d19dd 1339 }
c62fbfe1 1340
a51ea417 1341end:
0607ebbf 1342 scm_putc_unlocked (tlr, port);
c62fbfe1 1343 pstate->top = floor + 2;
a51ea417 1344 return;
c62fbfe1
MD
1345
1346fancy_printing:
1347 {
c014a02e 1348 long n = pstate->length;
c62fbfe1
MD
1349
1350 scm_iprin1 (SCM_CAR (exp), port, pstate);
1351 exp = SCM_CDR (exp); --n;
d2e53ed6 1352 for (; scm_is_pair (exp); exp = SCM_CDR (exp))
c62fbfe1 1353 {
c014a02e 1354 register unsigned long i;
5ca6dc39 1355
c62fbfe1 1356 for (i = 0; i < pstate->top; ++i)
509759dd 1357 if (scm_is_eq (PSTATE_STACK_REF(pstate, i), exp))
c62fbfe1
MD
1358 goto fancy_circref;
1359 if (pstate->fancyp)
1360 {
1361 if (n == 0)
1362 {
0607ebbf 1363 scm_puts_unlocked (" ...", port);
c62fbfe1
MD
1364 goto skip_tail;
1365 }
1366 else
1367 --n;
1368 }
1369 PUSH_REF(pstate, exp);
1370 ++pstate->list_offset;
0607ebbf 1371 scm_putc_unlocked (' ', port);
c62fbfe1
MD
1372 /* CHECK_INTS; */
1373 scm_iprin1 (SCM_CAR (exp), port, pstate);
1374 }
1375 }
c96d76b8 1376 if (!SCM_NULL_OR_NIL_P (exp))
c62fbfe1 1377 {
0607ebbf 1378 scm_puts_unlocked (" . ", port);
c62fbfe1
MD
1379 scm_iprin1 (exp, port, pstate);
1380 }
1381skip_tail:
1382 pstate->list_offset -= pstate->top - floor - 2;
a51ea417 1383 goto end;
a51ea417 1384
c62fbfe1
MD
1385fancy_circref:
1386 pstate->list_offset -= pstate->top - floor - 2;
1387
1388circref:
0607ebbf 1389 scm_puts_unlocked (" . ", port);
c62fbfe1
MD
1390 print_circref (port, pstate, exp);
1391 goto end;
0f2d19dd
JB
1392}
1393
1394\f
1395
bb35f315
MV
1396int
1397scm_valid_oport_value_p (SCM val)
1398{
368cf54d
GB
1399 return (SCM_OPOUTPORTP (val)
1400 || (SCM_PORT_WITH_PS_P (val)
1401 && SCM_OPOUTPORTP (SCM_PORT_WITH_PS_PORT (val))));
bb35f315
MV
1402}
1403
8b840115 1404/* SCM_GPROC(s_write, "write", 1, 1, 0, scm_write, g_write); */
1cc91f1b 1405
0f2d19dd 1406SCM
1bbd0b84 1407scm_write (SCM obj, SCM port)
0f2d19dd
JB
1408{
1409 if (SCM_UNBNDP (port))
9de87eea 1410 port = scm_current_output_port ();
3eb7e6ee
JB
1411
1412 SCM_ASSERT (scm_valid_oport_value_p (port), port, SCM_ARG2, s_write);
bb35f315 1413
215fe3a8 1414 scm_dynwind_begin (0);
92c0ebac 1415 scm_dynwind_lock_port (SCM_COERCE_OUTPORT (port));
a51ea417 1416 scm_prin1 (obj, port, 1);
215fe3a8
AW
1417 scm_dynwind_end ();
1418
0f2d19dd
JB
1419 return SCM_UNSPECIFIED;
1420}
1421
1422
8b840115 1423/* SCM_GPROC(s_display, "display", 1, 1, 0, scm_display, g_display); */
1cc91f1b 1424
0f2d19dd 1425SCM
1bbd0b84 1426scm_display (SCM obj, SCM port)
0f2d19dd
JB
1427{
1428 if (SCM_UNBNDP (port))
9de87eea 1429 port = scm_current_output_port ();
3eb7e6ee
JB
1430
1431 SCM_ASSERT (scm_valid_oport_value_p (port), port, SCM_ARG2, s_display);
bb35f315 1432
215fe3a8 1433 scm_dynwind_begin (0);
92c0ebac 1434 scm_dynwind_lock_port (SCM_COERCE_OUTPORT (port));
a51ea417 1435 scm_prin1 (obj, port, 0);
215fe3a8
AW
1436 scm_dynwind_end ();
1437
0f2d19dd
JB
1438 return SCM_UNSPECIFIED;
1439}
1440
70d63753
GB
1441
1442SCM_DEFINE (scm_simple_format, "simple-format", 2, 0, 1,
1443 (SCM destination, SCM message, SCM args),
eca65e90
MG
1444 "Write @var{message} to @var{destination}, defaulting to\n"
1445 "the current output port.\n"
1446 "@var{message} can contain @code{~A} (was @code{%s}) and\n"
1447 "@code{~S} (was @code{%S}) escapes. When printed,\n"
1448 "the escapes are replaced with corresponding members of\n"
b7e64f8b 1449 "@var{args}:\n"
eca65e90
MG
1450 "@code{~A} formats using @code{display} and @code{~S} formats\n"
1451 "using @code{write}.\n"
1452 "If @var{destination} is @code{#t}, then use the current output\n"
1453 "port, if @var{destination} is @code{#f}, then return a string\n"
1454 "containing the formatted text. Does not add a trailing newline.")
70d63753
GB
1455#define FUNC_NAME s_scm_simple_format
1456{
dfd03fb9 1457 SCM port, answer = SCM_UNSPECIFIED;
70d63753
GB
1458 int fReturnString = 0;
1459 int writingp;
889975e5 1460 size_t start, p, end;
70d63753 1461
bc36d050 1462 if (scm_is_eq (destination, SCM_BOOL_T))
daba1a71 1463 {
9de87eea 1464 destination = port = scm_current_output_port ();
daba1a71 1465 }
7888309b 1466 else if (scm_is_false (destination))
daba1a71
MD
1467 {
1468 fReturnString = 1;
0b2c2ba3 1469 port = scm_mkstrport (SCM_INUM0, SCM_BOOL_F,
dfd03fb9
MD
1470 SCM_OPN | SCM_WRTNG,
1471 FUNC_NAME);
1472 destination = port;
daba1a71
MD
1473 }
1474 else
1475 {
1476 SCM_VALIDATE_OPORT_VALUE (1, destination);
dfd03fb9 1477 port = SCM_COERCE_OUTPORT (destination);
daba1a71
MD
1478 }
1479 SCM_VALIDATE_STRING (2, message);
af45e3b0 1480 SCM_VALIDATE_REST_ARGUMENT (args);
70d63753 1481
889975e5
MG
1482 p = 0;
1483 start = 0;
1484 end = scm_i_string_length (message);
b24b5e13 1485 for (p = start; p != end; ++p)
889975e5 1486 if (scm_i_string_ref (message, p) == '~')
70d63753 1487 {
b24b5e13 1488 if (++p == end)
6662998f
MV
1489 break;
1490
889975e5 1491 switch (scm_i_string_ref (message, p))
6662998f
MV
1492 {
1493 case 'A': case 'a':
1494 writingp = 0;
1495 break;
1496 case 'S': case 's':
1497 writingp = 1;
1498 break;
1499 case '~':
889975e5 1500 scm_lfwrite_substr (message, start, p, port);
6662998f
MV
1501 start = p + 1;
1502 continue;
1503 case '%':
889975e5 1504 scm_lfwrite_substr (message, start, p - 1, port);
dfd03fb9 1505 scm_newline (port);
6662998f
MV
1506 start = p + 1;
1507 continue;
1508 default:
1afff620 1509 SCM_MISC_ERROR ("FORMAT: Unsupported format option ~~~A - use (ice-9 format) instead",
889975e5 1510 scm_list_1 (SCM_MAKE_CHAR (scm_i_string_ref (message, p))));
6662998f
MV
1511
1512 }
70d63753 1513
6662998f 1514
d2e53ed6 1515 if (!scm_is_pair (args))
1afff620 1516 SCM_MISC_ERROR ("FORMAT: Missing argument for ~~~A",
889975e5 1517 scm_list_1 (SCM_MAKE_CHAR (scm_i_string_ref (message, p))));
6662998f 1518
889975e5 1519 scm_lfwrite_substr (message, start, p - 1, port);
dfd03fb9 1520 /* we pass destination here */
70d63753
GB
1521 scm_prin1 (SCM_CAR (args), destination, writingp);
1522 args = SCM_CDR (args);
1523 start = p + 1;
1524 }
6662998f 1525
889975e5 1526 scm_lfwrite_substr (message, start, p, port);
bc36d050 1527 if (!scm_is_eq (args, SCM_EOL))
1afff620
KN
1528 SCM_MISC_ERROR ("FORMAT: ~A superfluous arguments",
1529 scm_list_1 (scm_length (args)));
70d63753
GB
1530
1531 if (fReturnString)
1532 answer = scm_strport_to_string (destination);
1533
daba1a71 1534 return scm_return_first (answer, message);
70d63753
GB
1535}
1536#undef FUNC_NAME
1537
1538
3b3b36dd 1539SCM_DEFINE (scm_newline, "newline", 0, 1, 0,
b450f070 1540 (SCM port),
8f85c0c6
NJ
1541 "Send a newline to @var{port}.\n"
1542 "If @var{port} is omitted, send to the current output port.")
1bbd0b84 1543#define FUNC_NAME s_scm_newline
0f2d19dd
JB
1544{
1545 if (SCM_UNBNDP (port))
9de87eea 1546 port = scm_current_output_port ();
3eb7e6ee 1547
34d19ef6 1548 SCM_VALIDATE_OPORT_VALUE (1, port);
bb35f315 1549
0607ebbf 1550 scm_putc_unlocked ('\n', SCM_COERCE_OUTPORT (port));
0f2d19dd
JB
1551 return SCM_UNSPECIFIED;
1552}
1bbd0b84 1553#undef FUNC_NAME
0f2d19dd 1554
3b3b36dd 1555SCM_DEFINE (scm_write_char, "write-char", 1, 1, 0,
b450f070 1556 (SCM chr, SCM port),
eca65e90 1557 "Send character @var{chr} to @var{port}.")
1bbd0b84 1558#define FUNC_NAME s_scm_write_char
0f2d19dd
JB
1559{
1560 if (SCM_UNBNDP (port))
9de87eea 1561 port = scm_current_output_port ();
3eb7e6ee 1562
34d19ef6
HWN
1563 SCM_VALIDATE_CHAR (1, chr);
1564 SCM_VALIDATE_OPORT_VALUE (2, port);
07f49ac7
LC
1565
1566 port = SCM_COERCE_OUTPORT (port);
1567 if (!display_character (SCM_CHAR (chr), port,
478848cb 1568 PORT_CONVERSION_HANDLER (port)))
07f49ac7
LC
1569 scm_encoding_error (__func__, errno,
1570 "cannot convert to output locale",
6851d3be 1571 port, chr);
07f49ac7 1572
0f2d19dd
JB
1573 return SCM_UNSPECIFIED;
1574}
1bbd0b84 1575#undef FUNC_NAME
0f2d19dd 1576
0f2d19dd
JB
1577\f
1578
bb35f315 1579/* Call back to Scheme code to do the printing of special objects
c19bc088
MD
1580 * (like structs). SCM_PRINTER_APPLY applies PROC to EXP and a smob
1581 * containing PORT and PSTATE. This object can be used as the port for
1582 * display/write etc to continue the current print chain. The REVEALED
1583 * field of PSTATE is set to true to indicate that the print state has
1584 * escaped to Scheme and thus has to be freed by the GC.
1585 */
1586
92c2555f 1587scm_t_bits scm_tc16_port_with_ps;
c19bc088
MD
1588
1589/* Print exactly as the port itself would */
1590
1591static int
e841c3e0 1592port_with_ps_print (SCM obj, SCM port, scm_print_state *pstate)
c19bc088
MD
1593{
1594 obj = SCM_PORT_WITH_PS_PORT (obj);
62bd5d66 1595 return SCM_PORT_DESCRIPTOR (obj)->print (obj, port, pstate);
c19bc088 1596}
c4f37e80
MV
1597
1598SCM
1bbd0b84 1599scm_printer_apply (SCM proc, SCM exp, SCM port, scm_print_state *pstate)
c4f37e80 1600{
bb35f315 1601 pstate->revealed = 1;
dfd03fb9
MD
1602 return scm_call_2 (proc, exp,
1603 scm_i_port_with_print_state (port, pstate->handle));
c19bc088
MD
1604}
1605
dfd03fb9 1606SCM_DEFINE (scm_port_with_print_state, "port-with-print-state", 1, 1, 0,
1bbd0b84 1607 (SCM port, SCM pstate),
71331188 1608 "Create a new port which behaves like @var{port}, but with an\n"
dfd03fb9
MD
1609 "included print state @var{pstate}. @var{pstate} is optional.\n"
1610 "If @var{pstate} isn't supplied and @var{port} already has\n"
1611 "a print state, the old print state is reused.")
1bbd0b84 1612#define FUNC_NAME s_scm_port_with_print_state
c19bc088 1613{
34d19ef6 1614 SCM_VALIDATE_OPORT_VALUE (1, port);
dfd03fb9
MD
1615 if (!SCM_UNBNDP (pstate))
1616 SCM_VALIDATE_PRINTSTATE (2, pstate);
1617 return scm_i_port_with_print_state (port, pstate);
c19bc088 1618}
1bbd0b84 1619#undef FUNC_NAME
c19bc088 1620
a1ec6916 1621SCM_DEFINE (scm_get_print_state, "get-print-state", 1, 0, 0,
1bbd0b84 1622 (SCM port),
71331188
MG
1623 "Return the print state of the port @var{port}. If @var{port}\n"
1624 "has no associated print state, @code{#f} is returned.")
1bbd0b84 1625#define FUNC_NAME s_scm_get_print_state
c19bc088 1626{
368cf54d
GB
1627 if (SCM_PORT_WITH_PS_P (port))
1628 return SCM_PORT_WITH_PS_PS (port);
f5f2dcff 1629 if (SCM_OUTPUT_PORT_P (port))
368cf54d 1630 return SCM_BOOL_F;
276dd677 1631 SCM_WRONG_TYPE_ARG (1, port);
c4f37e80 1632}
1bbd0b84 1633#undef FUNC_NAME
bb35f315 1634
c4f37e80 1635\f
1cc91f1b 1636
0f2d19dd
JB
1637void
1638scm_init_print ()
0f2d19dd 1639{
231dd356 1640 SCM type;
d5cf5324 1641
231dd356
AW
1642 type = scm_make_vtable (scm_from_locale_string (SCM_PRINT_STATE_LAYOUT),
1643 SCM_BOOL_F);
4a655e50 1644 scm_set_struct_vtable_name_x (type, scm_from_latin1_symbol ("print-state"));
bb35f315 1645 scm_print_state_vtable = type;
c4f37e80 1646
c19bc088
MD
1647 /* Don't want to bind a wrapper class in GOOPS, so pass 0 as arg1. */
1648 scm_tc16_port_with_ps = scm_make_smob_type (0, 0);
e841c3e0 1649 scm_set_smob_print (scm_tc16_port_with_ps, port_with_ps_print);
81ae25da 1650
a0599745 1651#include "libguile/print.x"
475fa9a5 1652
8500b186
AW
1653 scm_init_opts (scm_print_options, scm_print_opts);
1654 scm_print_opts[SCM_PRINT_HIGHLIGHT_PREFIX_I].val =
1655 SCM_UNPACK (scm_from_locale_string ("{"));
1656 scm_print_opts[SCM_PRINT_HIGHLIGHT_SUFFIX_I].val =
1657 SCM_UNPACK (scm_from_locale_string ("}"));
475fa9a5 1658 scm_print_opts[SCM_PRINT_KEYWORD_STYLE_I].val = SCM_UNPACK (sym_reader);
0f2d19dd 1659}
89e00824
ML
1660
1661/*
1662 Local Variables:
1663 c-file-style: "gnu"
1664 End:
1665*/