blob: 5e85bccbc4f0f3415edb8ca05535b35a263943e0 [file] [log] [blame]
Guido van Rossum16b8f301992-04-13 18:22:53 +00001/**********************************************************
2Copyright 1991, 1992 by Stichting Mathematisch Centrum, Amsterdam, The
3Netherlands.
4
5 All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Stichting Mathematisch
12Centrum or CWI not be used in advertising or publicity pertaining to
13distribution of the software without specific, written prior permission.
14
15STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23******************************************************************/
24
25/* CD module -- interface to Mark Callow's and Roger Chickering's */
26 /* CD Audio Library (CD). */
27
28#include <sys/types.h>
29#include <cdaudio.h>
30#include "allobjects.h"
31#include "import.h"
32#include "modsupport.h"
33#include "compile.h"
34#include "ceval.h"
35
36#define NCALLBACKS 8
37
38typedef struct {
39 OB_HEAD
40 CDPLAYER *ob_cdplayer;
41} cdplayerobject;
42
43#define CheckPlayer(self) if ((self)->ob_cdplayer == NULL) { \
44 err_setstr(RuntimeError, "no player active"); \
45 return NULL; \
46 }
47#define CheckParser(self) if ((self)->ob_cdparser == NULL) { \
48 err_setstr(RuntimeError, "no parser active"); \
49 return NULL; \
50 }
51
52static object *
53CD_allowremoval(self, args)
54 cdplayerobject *self;
55 object *args;
56{
57 CheckPlayer(self);
58
59 if (!getnoarg(args))
60 return NULL;
61
62 CDallowremoval(self->ob_cdplayer);
63
64 INCREF(None);
65 return None;
66}
67
68static object *
69CD_preventremoval(self, args)
70 cdplayerobject *self;
71 object *args;
72{
73 CheckPlayer(self);
74
75 if (!getnoarg(args))
76 return NULL;
77
78 CDpreventremoval(self->ob_cdplayer);
79
80 INCREF(None);
81 return None;
82}
83
84static object *
85CD_getvolume(self, args)
86 cdplayerobject *self;
87 object *args;
88{
89 CDVOLUME vol;
90 int retval;
91
92 CheckPlayer(self);
93
94 if (!getnoarg(args))
95 return NULL;
96
97#if 0
98 if (!CDgetvolume(self->ob_cdplayer, &vol)) {
99 err_setstr(RuntimeError, "getvolume failed");
100 return NULL;
101 }
102#endif
103
104 return mkvalue("(iiiii)", CDgetvolume(self->ob_cdplayer, &vol), \
105 vol.chan0, vol.chan1, vol.chan2, vol.chan3);
106}
107
108static object *
109CD_setvolume(self, args)
110 cdplayerobject *self;
111 object *args;
112{
113 CDVOLUME vol;
114 int retval;
115
116 CheckPlayer(self);
117
118 if (!getargs(args, "(bbbb)", &vol.chan0, &vol.chan1, \
119 &vol.chan2, &vol.chan3))
120 return NULL;
121
122 if (!CDsetvolume(self->ob_cdplayer, &vol)) {
123 err_setstr(RuntimeError, "setvolume failed");
124 return NULL;
125 }
126
127 INCREF(None);
128 return None;
129}
130
131static object *
132CD_bestreadsize(self, args)
133 cdplayerobject *self;
134 object *args;
135{
136 CheckPlayer(self);
137
138 if (!getnoarg(args))
139 return NULL;
140
141 return newintobject((long) CDbestreadsize(self->ob_cdplayer));
142}
143
144static object *
145CD_close(self, args)
146 cdplayerobject *self;
147 object *args;
148{
149 CheckPlayer(self);
150
151 if (!getnoarg(args))
152 return NULL;
153
154 if (!CDclose(self->ob_cdplayer)) {
155 err_errno(RuntimeError); /* XXX - ??? */
156 return NULL;
157 }
158 self->ob_cdplayer = NULL;
159
160 INCREF(None);
161 return None;
162}
163
164static object *
165CD_eject(self, args)
166 cdplayerobject *self;
167 object *args;
168{
169 CheckPlayer(self);
170
171 if (!getnoarg(args))
172 return NULL;
173
174 if (!CDeject(self->ob_cdplayer)) {
175 err_setstr(RuntimeError, "eject failed");
176 return NULL;
177 }
178
179 INCREF(None);
180 return None;
181}
182
183static object *
184CD_getstatus(self, args)
185 cdplayerobject *self;
186 object *args;
187{
188 CDSTATUS status;
189
190 CheckPlayer(self);
191
192 if (!getnoarg(args))
193 return NULL;
194
195 if (!CDgetstatus(self->ob_cdplayer, &status)) {
196 err_errno(RuntimeError); /* XXX - ??? */
197 return NULL;
198 }
199
Guido van Rossumece6efe1992-04-15 15:56:11 +0000200 return mkvalue("(ii(iii)(iii)(iii)iiii(iii))", status.state,
201 status.track, status.min, status.sec, status.frame,
202 status.abs_min, status.abs_sec, status.abs_frame,
203 status.total_min, status.total_sec, status.total_frame,
204 status.first, status.last, status.scsi_audio,
205 status.cur_block, status.polyfilla[0],
206 status.polyfilla[1], status.polyfilla[2]);
Guido van Rossum16b8f301992-04-13 18:22:53 +0000207}
208
209static object *
210CD_gettrackinfo(self, args)
211 cdplayerobject *self;
212 object *args;
213{
214 int track;
215 CDTRACKINFO info;
216
217 CheckPlayer(self);
218
219 if (!getargs(args, "i", &track))
220 return NULL;
221
222 if (!CDgettrackinfo(self->ob_cdplayer, track, &info)) {
223 err_setstr(RuntimeError, "gettrackinfo failed");
224 return NULL;
225 }
226
Guido van Rossumece6efe1992-04-15 15:56:11 +0000227 return mkvalue("((iii)(iii))",
Guido van Rossum16b8f301992-04-13 18:22:53 +0000228 info.start_min, info.start_sec, info.start_frame,
229 info.total_min, info.total_sec, info.total_frame);
230}
231
232static object *
233CD_msftoblock(self, args)
234 cdplayerobject *self;
235 object *args;
236{
237 int min, sec, frame;
238 unsigned long block;
239
240 CheckPlayer(self);
241
242 if (!getargs(args, "(iii)", &min, &sec, &frame))
243 return NULL;
244
245 block = CDmsftoblock(self->ob_cdplayer, min, sec, frame);
246 return newintobject((long) block);
247}
248
249static object *
250CD_play(self, args)
251 cdplayerobject *self;
252 object *args;
253{
254 int start, play;
255
256 CheckPlayer(self);
257
258 if (!getargs(args, "(ii)", &start, &play))
259 return NULL;
260
261 if (!CDplay(self->ob_cdplayer, start, play)) {
262 err_setstr(RuntimeError, "play failed");
263 return NULL;
264 }
265
266 INCREF(None);
267 return None;
268}
269
270static object *
271CD_playabs(self, args)
272 cdplayerobject *self;
273 object *args;
274{
275 int min, sec, frame, play;
276
277 CheckPlayer(self);
278
279 if (!getargs(args, "(iiii)", &min, &sec, &frame, &play))
280 return NULL;
281
282 if (!CDplayabs(self->ob_cdplayer, min, sec, frame, play)) {
283 err_setstr(RuntimeError, "playabs failed");
284 return NULL;
285 }
286
287 INCREF(None);
288 return None;
289}
290
291static object *
292CD_playtrack(self, args)
293 cdplayerobject *self;
294 object *args;
295{
296 int start, play;
297
298 CheckPlayer(self);
299
300 if (!getargs(args, "(ii)", &start, &play))
301 return NULL;
302
303 if (!CDplaytrack(self->ob_cdplayer, start, play)) {
304 err_setstr(RuntimeError, "playtrack failed");
305 return NULL;
306 }
307
308 INCREF(None);
309 return None;
310}
311
312static object *
313CD_playtrackabs(self, args)
314 cdplayerobject *self;
315 object *args;
316{
317 int track, min, sec, frame, play;
318
319 CheckPlayer(self);
320
321 if (!getargs(args, "(iiiii)", &track, &min, &sec, &frame, &play))
322 return NULL;
323
324 if (!CDplaytrackabs(self->ob_cdplayer, track, min, sec, frame, play)) {
325 err_setstr(RuntimeError, "playtrackabs failed");
326 return NULL;
327 }
328
329 INCREF(None);
330 return None;
331}
332
333static object *
334CD_readda(self, args)
335 cdplayerobject *self;
336 object *args;
337{
338 int numframes, n;
339 object *result;
340
341 CheckPlayer(self);
342
343 if (!getargs(args, "i", &numframes))
344 return NULL;
345
346 result = newsizedstringobject(NULL, numframes * sizeof(CDFRAME));
347 if (result == NULL)
348 return NULL;
349
350 n = CDreadda(self->ob_cdplayer, (CDFRAME *) getstringvalue(result), numframes);
351 if (n == -1) {
352 DECREF(result);
353 err_errno(RuntimeError); /* XXX - ??? (seems to work) */
354 return NULL;
355 }
356 if (n < numframes)
357 if (resizestring(&result, n * sizeof(CDFRAME)))
358 return NULL;
359
360 return result;
361}
362
363static object *
364CD_seek(self, args)
365 cdplayerobject *self;
366 object *args;
367{
368 int min, sec, frame;
369 long block;
370
371 CheckPlayer(self);
372
373 if (!getargs(args, "(iii)", &min, &sec, &frame))
374 return NULL;
375
376 block = CDseek(self->ob_cdplayer, min, sec, frame);
377 if (block == -1) {
378 err_errno(RuntimeError);
379 return NULL;
380 }
381
382 return newintobject(block);
383}
384
385static object *
386CD_seektrack(self, args)
387 cdplayerobject *self;
388 object *args;
389{
390 int track;
391 long block;
392
393 CheckPlayer(self);
394
395 if (!getargs(args, "i", &track))
396 return NULL;
397
398 block = CDseektrack(self->ob_cdplayer, track);
399 if (block == -1) {
400 err_errno(RuntimeError);
401 return NULL;
402 }
403
404 return newintobject(block);
405}
406
407static object *
408CD_stop(self, args)
409 cdplayerobject *self;
410 object *args;
411{
412 CheckPlayer(self);
413
414 if (!getnoarg(args))
415 return NULL;
416
417 if (!CDstop(self->ob_cdplayer)) {
418 err_setstr(RuntimeError, "stop failed");
419 return NULL;
420 }
421
422 INCREF(None);
423 return None;
424}
425
426static object *
427CD_togglepause(self, args)
428 cdplayerobject *self;
429 object *args;
430{
431 CheckPlayer(self);
432
433 if (!getnoarg(args))
434 return NULL;
435
436 if (!CDtogglepause(self->ob_cdplayer)) {
437 err_setstr(RuntimeError, "togglepause failed");
438 return NULL;
439 }
440
441 INCREF(None);
442 return None;
443}
444
445static struct methodlist cdplayer_methods[] = {
446 {"allowremoval", CD_allowremoval},
447 {"bestreadsize", CD_bestreadsize},
448 {"close", CD_close},
449 {"eject", CD_eject},
450 {"getstatus", CD_getstatus},
451 {"gettrackinfo", CD_gettrackinfo},
452 {"getvolume", CD_getvolume},
453 {"msftoblock", CD_msftoblock},
454 {"play", CD_play},
455 {"playabs", CD_playabs},
456 {"playtrack", CD_playtrack},
457 {"playtrackabs", CD_playtrackabs},
458 {"preventremoval", CD_preventremoval},
459 {"readda", CD_readda},
460 {"seek", CD_seek},
461 {"seektrack", CD_seektrack},
462 {"setvolume", CD_setvolume},
463 {"stop", CD_stop},
464 {"togglepause", CD_togglepause},
465 {NULL, NULL} /* sentinel */
466};
467
468static void
469cdplayer_dealloc(self)
470 cdplayerobject *self;
471{
472 if (self->ob_cdplayer != NULL)
473 CDclose(self->ob_cdplayer);
474 DEL(self);
475}
476
477static object *
478cdplayer_getattr(cdp, name)
479 cdplayerobject *cdp;
480 char *name;
481{
482 return findmethod(cdplayer_methods, (object *)cdp, name);
483}
484
485typeobject CdPlayertype = {
486 OB_HEAD_INIT(&Typetype)
487 0, /*ob_size*/
488 "cdplayer", /*tp_name*/
489 sizeof(cdplayerobject), /*tp_size*/
490 0, /*tp_itemsize*/
491 /* methods */
492 cdplayer_dealloc, /*tp_dealloc*/
493 0, /*tp_print*/
494 cdplayer_getattr, /*tp_getattr*/
495 0, /*tp_setattr*/
496 0, /*tp_compare*/
497 0, /*tp_repr*/
498};
499
500static object *
501newcdplayerobject(cdp)
502 CDPLAYER *cdp;
503{
504 cdplayerobject *p;
505
506 p = NEWOBJ(cdplayerobject, &CdPlayertype);
507 if (p == NULL)
508 return NULL;
509 p->ob_cdplayer = cdp;
510 return (object *) p;
511}
512
513static object *
514CD_open(self, args)
515 object *self, *args;
516{
517 char *dev, *direction;
518 CDPLAYER *cdp;
519
520 /*
521 * Variable number of args.
522 * First defaults to "None", second defaults to "r".
523 */
524 dev = NULL;
525 direction = "r";
526 if (!getnoarg(args)) {
527 err_clear();
528 if (!getargs(args, "z", &dev)) {
529 err_clear();
530 if (!getargs(args, "(zs)", &dev, &direction))
531 return NULL;
532 }
533 }
534
535 cdp = CDopen(dev, direction);
536 if (cdp == NULL) {
537 err_errno(RuntimeError);
538 return NULL;
539 }
540
541 return newcdplayerobject(cdp);
542}
543
544typedef struct {
545 OB_HEAD
546 CDPARSER *ob_cdparser;
547 struct {
548 object *ob_cdcallback;
549 object *ob_cdcallbackarg;
550 } ob_cdcallbacks[NCALLBACKS];
551} cdparserobject;
552
553static void
554CD_callback(arg, type, data)
555 void *arg;
556 CDDATATYPES type;
557 void *data;
558{
559 object *result, *args, *v;
560 char *p;
561 int i;
562 cdparserobject *self;
563
564 self = (cdparserobject *) arg;
565 args = newtupleobject(3);
566 if (args == NULL)
567 return;
568 INCREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
569 settupleitem(args, 0, self->ob_cdcallbacks[type].ob_cdcallbackarg);
570 settupleitem(args, 1, newintobject((long) type));
571 switch (type) {
572 case cd_audio:
573 v = newsizedstringobject(data, CDDA_DATASIZE);
574 break;
575 case cd_pnum:
576 case cd_index:
577 v = newintobject(((CDPROGNUM *) data)->value);
578 break;
579 case cd_ptime:
580 case cd_atime:
Guido van Rossumece6efe1992-04-15 15:56:11 +0000581#define ptr ((struct cdtimecode *) data)
582 v = mkvalue("(iii)", ptr->mhi * 10 + ptr->mlo,
583 ptr->shi * 10 + ptr->slo,
584 ptr->fhi * 10 + ptr->flo);
585#undef ptr
Guido van Rossum16b8f301992-04-13 18:22:53 +0000586 break;
587 case cd_catalog:
588 v = newsizedstringobject(NULL, 13);
589 p = getstringvalue(v);
590 for (i = 0; i < 13; i++)
591 *p++ = ((char *) data)[i] + '0';
592 break;
593 case cd_ident:
594 v = newsizedstringobject(NULL, 12);
595 p = getstringvalue(v);
596 CDsbtoa(p, ((struct cdident *) data)->country, 2);
597 p += 2;
598 CDsbtoa(p, ((struct cdident *) data)->owner, 3);
599 p += 3;
600 *p++ = ((struct cdident *) data)->year[0] + '0';
601 *p++ = ((struct cdident *) data)->year[1] + '0';
602 *p++ = ((struct cdident *) data)->serial[0] + '0';
603 *p++ = ((struct cdident *) data)->serial[1] + '0';
604 *p++ = ((struct cdident *) data)->serial[2] + '0';
605 *p++ = ((struct cdident *) data)->serial[3] + '0';
606 *p++ = ((struct cdident *) data)->serial[4] + '0';
607 break;
608 case cd_control:
609 v = newintobject((long) *((unchar *) data));
610 break;
611 }
612 settupleitem(args, 2, v);
613 if (err_occurred()) {
614 DECREF(args);
615 return;
616 }
617
618 result = call_object(self->ob_cdcallbacks[type].ob_cdcallback, args);
619 DECREF(args);
620 XDECREF(result);
621}
622
623static object *
624CD_deleteparser(self, args)
625 cdparserobject *self;
626 object *args;
627{
628 int i;
629
630 CheckParser(self);
631
632 if (!getnoarg(args))
633 return NULL;
634
635 CDdeleteparser(self->ob_cdparser);
636 self->ob_cdparser = NULL;
637
638 /* no sense in keeping the callbacks, so remove them */
639 for (i = 0; i < NCALLBACKS; i++) {
640 XDECREF(self->ob_cdcallbacks[i].ob_cdcallback);
641 self->ob_cdcallbacks[i].ob_cdcallback = NULL;
642 XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg);
643 self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
644 }
645
646 INCREF(None);
647 return None;
648}
649
650static object *
651CD_parseframe(self, args)
652 cdparserobject *self;
653 object *args;
654{
655 char *cdfp;
656 int length;
657 CDFRAME *p;
658
659 CheckParser(self);
660
661 if (!getargs(args, "s#", &cdfp, &length))
662 return NULL;
663
664 if (length % sizeof(CDFRAME) != 0) {
665 err_setstr(RuntimeError, "bad length");
666 return NULL;
667 }
668
669 p = (CDFRAME *) cdfp;
670 while (length > 0) {
671 CDparseframe(self->ob_cdparser, p);
672 length -= sizeof(CDFRAME);
673 p++;
674 if (err_occurred())
675 return NULL;
676 }
677
678 INCREF(None);
679 return None;
680}
681
682static object *
683CD_removecallback(self, args)
684 cdparserobject *self;
685 object *args;
686{
687 int type;
688
689 CheckParser(self);
690
691 if (!getargs(args, "i", &type))
692 return NULL;
693
694 CDremovecallback(self->ob_cdparser, (CDDATATYPES) type);
695
696 XDECREF(self->ob_cdcallbacks[type].ob_cdcallback);
697 self->ob_cdcallbacks[type].ob_cdcallback = NULL;
698
699 XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
700 self->ob_cdcallbacks[type].ob_cdcallbackarg = NULL;
701
702 INCREF(None);
703 return None;
704}
705
706static object *
707CD_resetparser(self, args)
708 cdparserobject *self;
709 object *args;
710{
711 CheckParser(self);
712
713 if (!getnoarg(args))
714 return NULL;
715
716 CDresetparser(self->ob_cdparser);
717
718 INCREF(None);
719 return None;
720}
721
722static object *
723CD_setcallback(self, args)
724 cdparserobject *self;
725 object *args;
726{
727 int type;
728 object *funcobject, *funcargobject;
729
730 CheckParser(self);
731
732 /* XXX - more work here */
733 if (!getargs(args, "(iOO)", &type, &funcobject, &funcargobject))
734 return NULL;
735
736 if (type < 0 || type >= NCALLBACKS) {
737 err_setstr(RuntimeError, "bad type");
738 return NULL;
739 }
740
741 CDsetcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback, (void *) self);
742 XDECREF(self->ob_cdcallbacks[type].ob_cdcallback);
743 INCREF(funcobject);
744 self->ob_cdcallbacks[type].ob_cdcallback = funcobject;
745 XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
746 INCREF(funcargobject);
747 self->ob_cdcallbacks[type].ob_cdcallbackarg = funcargobject;
748
749 INCREF(None);
750 return None;
751}
752
753static struct methodlist cdparser_methods[] = {
754 {"deleteparser", CD_deleteparser},
755 {"parseframe", CD_parseframe},
756 {"removecallback", CD_removecallback},
757 {"resetparser", CD_resetparser},
758 {"setcallback", CD_setcallback},
759 {NULL, NULL} /* sentinel */
760};
761
762static void
763cdparser_dealloc(self)
764 cdparserobject *self;
765{
766 int i;
767
768 for (i = 0; i < NCALLBACKS; i++) {
769 XDECREF(self->ob_cdcallbacks[i].ob_cdcallback);
770 self->ob_cdcallbacks[i].ob_cdcallback = NULL;
771 XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg);
772 self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
773 }
774 CDdeleteparser(self->ob_cdparser);
775 DEL(self);
776}
777
778static object *
779cdparser_getattr(cdp, name)
780 cdparserobject *cdp;
781 char *name;
782{
783 return findmethod(cdparser_methods, (object *)cdp, name);
784}
785
786typeobject CdParsertype = {
787 OB_HEAD_INIT(&Typetype)
788 0, /*ob_size*/
789 "cdparser", /*tp_name*/
790 sizeof(cdparserobject), /*tp_size*/
791 0, /*tp_itemsize*/
792 /* methods */
793 cdparser_dealloc, /*tp_dealloc*/
794 0, /*tp_print*/
795 cdparser_getattr, /*tp_getattr*/
796 0, /*tp_setattr*/
797 0, /*tp_compare*/
798 0, /*tp_repr*/
799};
800
801static object *
802newcdparserobject(cdp)
803 CDPARSER *cdp;
804{
805 cdparserobject *p;
806 int i;
807
808 p = NEWOBJ(cdparserobject, &CdParsertype);
809 if (p == NULL)
810 return NULL;
811 p->ob_cdparser = cdp;
812 for (i = 0; i < NCALLBACKS; i++) {
813 p->ob_cdcallbacks[i].ob_cdcallback = NULL;
814 p->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
815 }
816 return (object *) p;
817}
818
819static object *
820CD_createparser(self, args)
821 object *self, *args;
822{
823 CDPARSER *cdp;
824
825 if (!getnoarg(args))
826 return NULL;
827 cdp = CDcreateparser();
828 if (cdp == NULL) {
829 err_setstr(RuntimeError, "createparser failed");
830 return NULL;
831 }
832
833 return newcdparserobject(cdp);
834}
835
836static object *
837CD_sbtoa(self, args)
838 object *self;
839 object *args;
840{
841 char *sb;
842 int length;
843 object *result;
844
845 if (!getargs(args, "s#", &sb, &length))
846 return NULL;
847 result = newsizedstringobject(NULL, length);
848 CDsbtoa(getstringvalue(result), (unchar *) sb, length);
849 return result;
850}
851
852static object *
853CD_timetoa(self, args)
854 object *self;
855 object *args;
856{
857 char *tc;
858 int length;
859 object *result;
860
861 if (!getargs(args, "s#", &tc, &length))
862 return NULL;
863
864 if (length != sizeof(struct cdtimecode)) {
865 err_setstr(RuntimeError, "bad length");
866 return NULL;
867 }
868
869 result = newsizedstringobject(NULL, 8);
870 CDtimetoa(getstringvalue(result), (struct cdtimecode *) tc);
871 return result;
872}
873
874static struct methodlist CD_methods[] = {
875 {"sbtoa", CD_sbtoa},
876 {"open", CD_open},
877 {"createparser",CD_createparser},
878 {"timetoa", CD_timetoa},
879 {NULL, NULL} /* Sentinel */
880};
881
882void
883initcd()
884{
885 (void) initmodule("cd", CD_methods);
886}