* objects.c: #include "smob.h";
[bpt/guile.git] / libguile / struct.c
CommitLineData
7dc6e754 1/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
0f2d19dd
JB
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
82892bed
JB
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
0f2d19dd
JB
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
82892bed 40 * If you do not wish that, delete this exception notice. */
0f2d19dd
JB
41\f
42
43#include <stdio.h>
44#include "_scm.h"
20e6290e 45#include "chars.h"
bafcafb2 46#include "genio.h"
916d65b1 47#include "eval.h"
20e6290e
JB
48
49#include "struct.h"
0f2d19dd 50
95b88819
GH
51#ifdef HAVE_STRING_H
52#include <string.h>
53#endif
54
0f2d19dd
JB
55\f
56
57static SCM required_vtable_fields = SCM_BOOL_F;
58static int struct_num = 0;
59
60\f
61SCM_PROC (s_struct_make_layout, "make-struct-layout", 1, 0, 0, scm_make_struct_layout);
1cc91f1b 62
0f2d19dd
JB
63SCM
64scm_make_struct_layout (fields)
65 SCM fields;
0f2d19dd
JB
66{
67 SCM new_sym;
68 SCM_ASSERT (SCM_NIMP (fields) && SCM_ROSTRINGP (fields),
69 fields, SCM_ARG1, s_struct_make_layout);
70
71 {
72 char * field_desc;
73 int len;
74 int x;
75
76 len = SCM_ROLENGTH (fields);
77 field_desc = SCM_ROCHARS (fields);
78 SCM_ASSERT (!(len & 1), fields, "odd length field specification", s_struct_make_layout);
79
80 for (x = 0; x < len; x += 2)
81 {
82 switch (field_desc[x])
83 {
84 case 'u':
85 case 'p':
86#if 0
87 case 'i':
88 case 'd':
89#endif
90 case 's':
91 break;
92 default:
93 SCM_ASSERT (0, SCM_MAKICHR (field_desc[x]) , "unrecognized field type", s_struct_make_layout);
94 }
95
96 switch (field_desc[x + 1])
97 {
98 case 'w':
2c36c351 99 SCM_ASSERT (field_desc[x] != 's', SCM_MAKICHR (field_desc[x + 1]),
0f2d19dd
JB
100 "self fields not writable", s_struct_make_layout);
101
102 case 'r':
103 case 'o':
104 break;
2c36c351
MD
105 case 'R':
106 case 'W':
107 case 'O':
108 SCM_ASSERT (field_desc[x] != 's', SCM_MAKICHR (field_desc[x + 1]),
109 "self fields not allowed in tail array",
110 s_struct_make_layout);
111 SCM_ASSERT (x == len - 2, SCM_MAKICHR (field_desc[x + 1]),
112 "tail array field must be last field in layout",
113 s_struct_make_layout);
114 break;
0f2d19dd
JB
115 default:
116 SCM_ASSERT (0, SCM_MAKICHR (field_desc[x]) , "unrecognized ref specification", s_struct_make_layout);
117 }
118#if 0
119 if (field_desc[x] == 'd')
120 {
121 SCM_ASSERT (field_desc[x + 2] == '-', SCM_MAKINUM (x / 2), "missing dash field", s_struct_make_layout);
122 x += 2;
123 goto recheck_ref;
124 }
125#endif
126 }
127 new_sym = SCM_CAR (scm_intern_obarray (field_desc, len, SCM_BOOL_F));
128 }
129 return scm_return_first (new_sym, fields);
130}
131
132\f
133
134
1cc91f1b 135
a5bfe84d
MD
136void
137scm_struct_init (handle, tail_elts, inits)
0f2d19dd 138 SCM handle;
2c36c351 139 int tail_elts;
0f2d19dd 140 SCM inits;
0f2d19dd
JB
141{
142 SCM layout;
143 SCM * data;
144 unsigned char * fields_desc;
35de7ebe 145 unsigned char prot = 0;
0f2d19dd
JB
146 int n_fields;
147 SCM * mem;
2c36c351
MD
148 int tailp = 0;
149
0f2d19dd
JB
150 layout = SCM_STRUCT_LAYOUT (handle);
151 data = SCM_STRUCT_DATA (handle);
2c36c351 152 fields_desc = (unsigned char *) SCM_CHARS (layout) - 2;
0f2d19dd
JB
153 n_fields = SCM_LENGTH (layout) / 2;
154 mem = SCM_STRUCT_DATA (handle);
155 while (n_fields)
156 {
2c36c351
MD
157 if (!tailp)
158 {
159 fields_desc += 2;
160 prot = fields_desc[1];
161 if (SCM_LAYOUT_TAILP (prot))
162 {
163 tailp = 1;
164 prot = prot == 'R' ? 'r' : prot == 'W' ? 'w' : 'o';
165 *mem++ = tail_elts;
166 n_fields += tail_elts - 1;
167 if (n_fields == 0)
168 break;
169 }
170 }
171
0f2d19dd
JB
172 switch (*fields_desc)
173 {
174#if 0
175 case 'i':
2c36c351 176 if ((prot != 'r' && prot != 'w') || inits == SCM_EOL)
0f2d19dd
JB
177 *mem = 0;
178 else
179 {
a5bfe84d 180 *mem = scm_num2long (SCM_CAR (inits), SCM_ARGn, "scm_struct_init");
0f2d19dd
JB
181 inits = SCM_CDR (inits);
182 }
183 break;
184#endif
185
186 case 'u':
2c36c351 187 if ((prot != 'r' && prot != 'w') || inits == SCM_EOL)
0f2d19dd
JB
188 *mem = 0;
189 else
190 {
a5bfe84d 191 *mem = scm_num2ulong (SCM_CAR (inits), SCM_ARGn, "scm_struct_init");
0f2d19dd
JB
192 inits = SCM_CDR (inits);
193 }
194 break;
195
196 case 'p':
2c36c351 197 if ((prot != 'r' && prot != 'w') || inits == SCM_EOL)
916d65b1 198 *mem = SCM_BOOL_F;
0f2d19dd
JB
199 else
200 {
201 *mem = SCM_CAR (inits);
202 inits = SCM_CDR (inits);
203 }
204
205 break;
206
207#if 0
208 case 'd':
2c36c351 209 if ((prot != 'r' && prot != 'w') || inits == SCM_EOL)
0f2d19dd
JB
210 *((double *)mem) = 0.0;
211 else
212 {
a5bfe84d 213 *mem = scm_num2dbl (SCM_CAR (inits), "scm_struct_init");
0f2d19dd
JB
214 inits = SCM_CDR (inits);
215 }
216 fields_desc += 2;
217 break;
218#endif
219
220 case 's':
221 *mem = handle;
222 break;
223 }
224
0f2d19dd
JB
225 n_fields--;
226 mem++;
227 }
228}
229
230
231SCM_PROC (s_struct_p, "struct?", 1, 0, 0, scm_struct_p);
1cc91f1b 232
0f2d19dd
JB
233SCM
234scm_struct_p (x)
235 SCM x;
0f2d19dd
JB
236{
237 return ((SCM_NIMP (x) && SCM_STRUCTP (x))
238 ? SCM_BOOL_T
239 : SCM_BOOL_F);
240}
241
242SCM_PROC (s_struct_vtable_p, "struct-vtable?", 1, 0, 0, scm_struct_vtable_p);
1cc91f1b 243
0f2d19dd
JB
244SCM
245scm_struct_vtable_p (x)
246 SCM x;
0f2d19dd
JB
247{
248 SCM layout;
249 SCM * mem;
250
251 if (SCM_IMP (x))
252 return SCM_BOOL_F;
253
254 if (!SCM_STRUCTP (x))
255 return SCM_BOOL_F;
256
257 layout = SCM_STRUCT_LAYOUT (x);
258
259 if (SCM_LENGTH (layout) < SCM_LENGTH (required_vtable_fields))
260 return SCM_BOOL_F;
261
262 if (strncmp (SCM_CHARS (layout), SCM_CHARS (required_vtable_fields),
263 SCM_LENGTH (required_vtable_fields)))
264 return SCM_BOOL_F;
265
266 mem = SCM_STRUCT_DATA (x);
267
268 if (mem[1] != 0)
269 return SCM_BOOL_F;
270
271 if (SCM_IMP (mem[0]))
272 return SCM_BOOL_F;
273
274 return (SCM_SYMBOLP (mem[0])
275 ? SCM_BOOL_T
276 : SCM_BOOL_F);
277}
278
14d1400f
JB
279
280/* All struct data must be allocated at an address whose bottom three
281 bits are zero. This is because the tag for a struct lives in the
282 bottom three bits of the struct's car, and the upper bits point to
283 the data of its vtable, which is a struct itself. Thus, if the
284 address of that data doesn't end in three zeros, tagging it will
285 destroy the pointer.
286
287 This function allocates a block of memory, and returns a pointer at
288 least scm_struct_n_extra_words words into the block. Furthermore,
289 it guarantees that that pointer's least three significant bits are
290 all zero.
291
292 The argument n_words should be the number of words that should
293 appear after the returned address. (That is, it shouldn't include
294 scm_struct_n_extra_words.)
295
296 This function initializes the following fields of the struct:
297
298 scm_struct_i_ptr --- the actual stort of the block of memory; the
299 address you should pass to 'free' to dispose of the block.
300 This field allows us to both guarantee that the returned
301 address is divisible by eight, and allow the GC to free the
302 block.
303
304 scm_struct_i_n_words --- the number of words allocated to the
305 block, including the extra fields. This is used by the GC.
306
307 scm_struct_i_tag --- a unique tag assigned to this struct,
308 allocated according to struct_num.
309
310 Ugh. */
311
312
a5bfe84d
MD
313SCM *
314scm_alloc_struct (int n_words, int n_extra, char *who)
14d1400f 315{
a5bfe84d 316 int size = sizeof (SCM) * (n_words + n_extra) + 7;
14d1400f
JB
317 SCM *block = (SCM *) scm_must_malloc (size, who);
318
319 /* Adjust the pointer to hide the extra words. */
a5bfe84d 320 SCM *p = block + n_extra;
14d1400f
JB
321
322 /* Adjust it even further so it's aligned on an eight-byte boundary. */
323 p = (SCM *) (((SCM) p + 7) & ~7);
324
325 /* Initialize a few fields as described above. */
326 p[scm_struct_i_ptr] = (SCM) block;
a5bfe84d 327 p[scm_struct_i_n_words] = (SCM) (n_words + n_extra);
14d1400f
JB
328 p[scm_struct_i_tag] = struct_num++;
329
330 return p;
331}
332
333
0f2d19dd 334SCM_PROC (s_make_struct, "make-struct", 2, 0, 1, scm_make_struct);
1cc91f1b 335
0f2d19dd
JB
336SCM
337scm_make_struct (vtable, tail_array_size, init)
338 SCM vtable;
339 SCM tail_array_size;
340 SCM init;
0f2d19dd
JB
341{
342 SCM layout;
343 int basic_size;
344 int tail_elts;
345 SCM * data;
346 SCM handle;
347
348 SCM_ASSERT ((SCM_BOOL_F != scm_struct_vtable_p (vtable)),
349 vtable, SCM_ARG1, s_make_struct);
14d1400f
JB
350 SCM_ASSERT (SCM_INUMP (tail_array_size), tail_array_size, SCM_ARG2,
351 s_make_struct);
0f2d19dd 352
4bfdf158 353 layout = SCM_STRUCT_DATA (vtable)[scm_vtable_index_layout];
0f2d19dd
JB
354 basic_size = SCM_LENGTH (layout) / 2;
355 tail_elts = SCM_INUM (tail_array_size);
356 SCM_NEWCELL (handle);
357 SCM_DEFER_INTS;
a5bfe84d
MD
358 if (SCM_STRUCT_DATA (vtable)[scm_struct_i_flags] & SCM_STRUCTF_ENTITY)
359 {
360 data = scm_alloc_struct (basic_size + tail_elts,
25c94826 361 scm_struct_n_extra_words + 5,
a5bfe84d
MD
362 "make-struct");
363 data[scm_struct_i_proc + 0] = SCM_BOOL_F;
364 data[scm_struct_i_proc + 1] = SCM_BOOL_F;
365 data[scm_struct_i_proc + 2] = SCM_BOOL_F;
366 data[scm_struct_i_proc + 3] = SCM_BOOL_F;
25c94826 367 data[scm_struct_i_setter] = SCM_BOOL_F;
a5bfe84d
MD
368 }
369 else
370 data = scm_alloc_struct (basic_size + tail_elts,
371 scm_struct_n_extra_words,
372 "make-struct");
0f2d19dd 373 SCM_SETCDR (handle, data);
35457f1e 374 SCM_SETCAR (handle, ((SCM)SCM_STRUCT_DATA (vtable)) + scm_tc3_cons_gloc);
a5bfe84d 375 scm_struct_init (handle, tail_elts, init);
0f2d19dd
JB
376 SCM_ALLOW_INTS;
377 return handle;
378}
379
380
381
382SCM_PROC (s_make_vtable_vtable, "make-vtable-vtable", 2, 0, 1, scm_make_vtable_vtable);
1cc91f1b 383
0f2d19dd
JB
384SCM
385scm_make_vtable_vtable (extra_fields, tail_array_size, init)
386 SCM extra_fields;
387 SCM tail_array_size;
388 SCM init;
0f2d19dd
JB
389{
390 SCM fields;
391 SCM layout;
392 int basic_size;
393 int tail_elts;
394 SCM * data;
395 SCM handle;
396
397 SCM_ASSERT (SCM_NIMP (extra_fields) && SCM_ROSTRINGP (extra_fields),
398 extra_fields, SCM_ARG1, s_make_vtable_vtable);
14d1400f
JB
399 SCM_ASSERT (SCM_INUMP (tail_array_size), tail_array_size, SCM_ARG2,
400 s_make_vtable_vtable);
0f2d19dd
JB
401
402 fields = scm_string_append (scm_listify (required_vtable_fields,
403 extra_fields,
404 SCM_UNDEFINED));
405 layout = scm_make_struct_layout (fields);
406 basic_size = SCM_LENGTH (layout) / 2;
407 tail_elts = SCM_INUM (tail_array_size);
408 SCM_NEWCELL (handle);
409 SCM_DEFER_INTS;
a5bfe84d
MD
410 data = scm_alloc_struct (basic_size + tail_elts,
411 scm_struct_n_extra_words,
412 "make-vtable-vtable");
0f2d19dd 413 SCM_SETCDR (handle, data);
35457f1e 414 SCM_SETCAR (handle, ((SCM)data) + scm_tc3_cons_gloc);
0f2d19dd 415 SCM_STRUCT_LAYOUT (handle) = layout;
a5bfe84d 416 scm_struct_init (handle, tail_elts, scm_cons (layout, init));
0f2d19dd
JB
417 SCM_ALLOW_INTS;
418 return handle;
419}
420
421\f
422
423
424SCM_PROC (s_struct_ref, "struct-ref", 2, 0, 0, scm_struct_ref);
1cc91f1b 425
0f2d19dd
JB
426SCM
427scm_struct_ref (handle, pos)
428 SCM handle;
429 SCM pos;
0f2d19dd 430{
5e840c2e 431 SCM answer = SCM_UNDEFINED;
0f2d19dd
JB
432 SCM * data;
433 SCM layout;
434 int p;
435 int n_fields;
436 unsigned char * fields_desc;
f3667f52 437 unsigned char field_type = 0;
0f2d19dd
JB
438
439
440 SCM_ASSERT (SCM_NIMP (handle) && SCM_STRUCTP (handle), handle,
441 SCM_ARG1, s_struct_ref);
442 SCM_ASSERT (SCM_INUMP (pos), pos, SCM_ARG2, s_struct_ref);
443
444 layout = SCM_STRUCT_LAYOUT (handle);
445 data = SCM_STRUCT_DATA (handle);
446 p = SCM_INUM (pos);
447
448 fields_desc = (unsigned char *)SCM_CHARS (layout);
bafcafb2 449 n_fields = data[scm_struct_i_n_words] - scm_struct_n_extra_words;
2c36c351
MD
450
451 SCM_ASSERT (p < n_fields, pos, SCM_OUTOFRANGE, s_struct_ref);
0f2d19dd 452
2c36c351
MD
453 if (p * 2 < SCM_LENGTH (layout))
454 {
455 unsigned char ref;
456 field_type = fields_desc[p * 2];
457 ref = fields_desc[p * 2 + 1];
458 if ((ref != 'r') && (ref != 'w'))
459 {
460 if ((ref == 'R') || (ref == 'W'))
461 field_type = 'u';
462 else
463 SCM_ASSERT (0, pos, "ref denied", s_struct_ref);
464 }
465 }
466 else if (fields_desc[SCM_LENGTH (layout) - 1] != 'O')
467 field_type = fields_desc[SCM_LENGTH (layout) - 2];
468 else
469 {
470 SCM_ASSERT (0, pos, "ref denied", s_struct_ref);
35de7ebe 471 abort ();
2c36c351
MD
472 }
473
0f2d19dd
JB
474 switch (field_type)
475 {
476 case 'u':
477 answer = scm_ulong2num (data[p]);
478 break;
479
480#if 0
481 case 'i':
482 answer = scm_long2num (data[p]);
483 break;
484
485 case 'd':
486 answer = scm_makdbl (*((double *)&(data[p])), 0.0);
487 break;
488#endif
489
490 case 's':
491 case 'p':
492 answer = data[p];
493 break;
494
495
496 default:
497 SCM_ASSERT (0, SCM_MAKICHR (field_type), "unrecognized field type", s_struct_ref);
498 break;
499 }
500
501 return answer;
502}
503
504
505SCM_PROC (s_struct_set_x, "struct-set!", 3, 0, 0, scm_struct_set_x);
1cc91f1b 506
0f2d19dd
JB
507SCM
508scm_struct_set_x (handle, pos, val)
509 SCM handle;
510 SCM pos;
511 SCM val;
0f2d19dd
JB
512{
513 SCM * data;
514 SCM layout;
515 int p;
516 int n_fields;
517 unsigned char * fields_desc;
f3667f52 518 unsigned char field_type = 0;
0f2d19dd
JB
519
520
521
522 SCM_ASSERT (SCM_NIMP (handle) && SCM_STRUCTP (handle), handle,
523 SCM_ARG1, s_struct_ref);
524 SCM_ASSERT (SCM_INUMP (pos), pos, SCM_ARG2, s_struct_ref);
525
526 layout = SCM_STRUCT_LAYOUT (handle);
527 data = SCM_STRUCT_DATA (handle);
528 p = SCM_INUM (pos);
529
530 fields_desc = (unsigned char *)SCM_CHARS (layout);
bafcafb2 531 n_fields = data[scm_struct_i_n_words] - scm_struct_n_extra_words;
0f2d19dd 532
2c36c351 533 SCM_ASSERT (p < n_fields, pos, SCM_OUTOFRANGE, s_struct_set_x);
0f2d19dd 534
2c36c351
MD
535 if (p * 2 < SCM_LENGTH (layout))
536 {
537 unsigned char set_x;
538 field_type = fields_desc[p * 2];
539 set_x = fields_desc [p * 2 + 1];
540 if (set_x != 'w')
541 SCM_ASSERT (0, pos, "set_x denied", s_struct_set_x);
542 }
543 else if (fields_desc[SCM_LENGTH (layout) - 1] == 'W')
544 field_type = fields_desc[SCM_LENGTH (layout) - 2];
545 else
546 {
547 SCM_ASSERT (0, pos, "set_x denied", s_struct_ref);
35de7ebe 548 abort ();
2c36c351
MD
549 }
550
0f2d19dd
JB
551 switch (field_type)
552 {
553 case 'u':
554 data[p] = (SCM)scm_num2ulong (val, (char *)SCM_ARG3, s_struct_set_x);
555 break;
556
557#if 0
558 case 'i':
559 data[p] = scm_num2long (val, (char *)SCM_ARG3, s_struct_set_x);
560 break;
561
562 case 'd':
563 *((double *)&(data[p])) = scm_num2dbl (val, (char *)SCM_ARG3);
564 break;
565#endif
566
567 case 'p':
568 data[p] = val;
569 break;
570
571 case 's':
572 SCM_ASSERT (0, SCM_MAKICHR (field_type), "self fields immutable", s_struct_set_x);
573 break;
574
575 default:
576 SCM_ASSERT (0, SCM_MAKICHR (field_type), "unrecognized field type", s_struct_set_x);
577 break;
578 }
579
580 return val;
581}
582
583
584SCM_PROC (s_struct_vtable, "struct-vtable", 1, 0, 0, scm_struct_vtable);
1cc91f1b 585
0f2d19dd
JB
586SCM
587scm_struct_vtable (handle)
588 SCM handle;
0f2d19dd
JB
589{
590 SCM_ASSERT (SCM_NIMP (handle) && SCM_STRUCTP (handle), handle,
591 SCM_ARG1, s_struct_vtable);
592 return SCM_STRUCT_VTABLE (handle);
593}
594
595
596SCM_PROC (s_struct_vtable_tag, "struct-vtable-tag", 1, 0, 0, scm_struct_vtable_tag);
1cc91f1b 597
0f2d19dd
JB
598SCM
599scm_struct_vtable_tag (handle)
600 SCM handle;
0f2d19dd
JB
601{
602 SCM_ASSERT (SCM_NIMP (handle) && (SCM_BOOL_F != scm_struct_vtable_p (handle)),
603 handle, SCM_ARG1, s_struct_vtable_tag);
604 return scm_long2num (SCM_STRUCT_DATA (handle)[-1]);
605}
606
607
608\f
609
bafcafb2
MV
610void
611scm_print_struct (exp, port, pstate)
612 SCM exp;
613 SCM port;
614 scm_print_state *pstate;
615{
4bfdf158
MD
616 if (SCM_NFALSEP (scm_procedure_p (SCM_STRUCT_PRINTER (exp))))
617 scm_printer_apply (SCM_STRUCT_PRINTER (exp), exp, port, pstate);
618 else
bafcafb2 619 {
b7f3516f 620 scm_lfwrite ("#<struct ", sizeof ("#<struct ") - 1, port);
4bfdf158 621 scm_intprint (SCM_STRUCT_VTABLE (exp), 16, port);
b7f3516f 622 scm_putc (':', port);
916d65b1 623 scm_intprint (exp, 16, port);
b7f3516f 624 scm_putc ('>', port);
bafcafb2 625 }
bafcafb2 626}
1cc91f1b 627
0f2d19dd
JB
628void
629scm_init_struct ()
0f2d19dd 630{
4bfdf158 631 required_vtable_fields = SCM_CAR (scm_intern_obarray ("pruosrpw", sizeof ("pruosrpw") - 1, SCM_BOOL_F));
0f2d19dd 632 scm_permanent_object (required_vtable_fields);
4bfdf158
MD
633 scm_sysintern ("vtable-index-layout", SCM_MAKINUM (scm_vtable_index_layout));
634 scm_sysintern ("vtable-index-vtable", SCM_MAKINUM (scm_vtable_index_vtable));
635 scm_sysintern ("vtable-index-printer", SCM_MAKINUM (scm_vtable_index_printer));
636 scm_sysintern ("vtable-offset-user", SCM_MAKINUM (scm_vtable_offset_user));
0f2d19dd
JB
637#include "struct.x"
638}