blob: d749adadb13137538b5798f2671eb16a161de130 [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
200 return mkvalue("(iiiiiiiiiiiiiiiiii)", status.state, status.track,
201 status.min, status.sec, status.frame, status.abs_min,
202 status.abs_sec, status.abs_frame, status.total_min,
203 status.total_sec, status.total_frame, status.first,
204 status.last, status.scsi_audio, status.cur_block,
205 status.polyfilla[0], status.polyfilla[1],
206 status.polyfilla[2]);
207}
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
227 return mkvalue("(iiiiii)",
228 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:
581 v = newsizedstringobject(NULL, 6);
582 p = getstringvalue(v);
583 *p++ = ((struct cdtimecode *) data)->mhi + '0';
584 *p++ = ((struct cdtimecode *) data)->mlo + '0';
585 *p++ = ((struct cdtimecode *) data)->shi + '0';
586 *p++ = ((struct cdtimecode *) data)->slo + '0';
587 *p++ = ((struct cdtimecode *) data)->fhi + '0';
588 *p++ = ((struct cdtimecode *) data)->flo + '0';
589 break;
590 case cd_catalog:
591 v = newsizedstringobject(NULL, 13);
592 p = getstringvalue(v);
593 for (i = 0; i < 13; i++)
594 *p++ = ((char *) data)[i] + '0';
595 break;
596 case cd_ident:
597 v = newsizedstringobject(NULL, 12);
598 p = getstringvalue(v);
599 CDsbtoa(p, ((struct cdident *) data)->country, 2);
600 p += 2;
601 CDsbtoa(p, ((struct cdident *) data)->owner, 3);
602 p += 3;
603 *p++ = ((struct cdident *) data)->year[0] + '0';
604 *p++ = ((struct cdident *) data)->year[1] + '0';
605 *p++ = ((struct cdident *) data)->serial[0] + '0';
606 *p++ = ((struct cdident *) data)->serial[1] + '0';
607 *p++ = ((struct cdident *) data)->serial[2] + '0';
608 *p++ = ((struct cdident *) data)->serial[3] + '0';
609 *p++ = ((struct cdident *) data)->serial[4] + '0';
610 break;
611 case cd_control:
612 v = newintobject((long) *((unchar *) data));
613 break;
614 }
615 settupleitem(args, 2, v);
616 if (err_occurred()) {
617 DECREF(args);
618 return;
619 }
620
621 result = call_object(self->ob_cdcallbacks[type].ob_cdcallback, args);
622 DECREF(args);
623 XDECREF(result);
624}
625
626static object *
627CD_deleteparser(self, args)
628 cdparserobject *self;
629 object *args;
630{
631 int i;
632
633 CheckParser(self);
634
635 if (!getnoarg(args))
636 return NULL;
637
638 CDdeleteparser(self->ob_cdparser);
639 self->ob_cdparser = NULL;
640
641 /* no sense in keeping the callbacks, so remove them */
642 for (i = 0; i < NCALLBACKS; i++) {
643 XDECREF(self->ob_cdcallbacks[i].ob_cdcallback);
644 self->ob_cdcallbacks[i].ob_cdcallback = NULL;
645 XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg);
646 self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
647 }
648
649 INCREF(None);
650 return None;
651}
652
653static object *
654CD_parseframe(self, args)
655 cdparserobject *self;
656 object *args;
657{
658 char *cdfp;
659 int length;
660 CDFRAME *p;
661
662 CheckParser(self);
663
664 if (!getargs(args, "s#", &cdfp, &length))
665 return NULL;
666
667 if (length % sizeof(CDFRAME) != 0) {
668 err_setstr(RuntimeError, "bad length");
669 return NULL;
670 }
671
672 p = (CDFRAME *) cdfp;
673 while (length > 0) {
674 CDparseframe(self->ob_cdparser, p);
675 length -= sizeof(CDFRAME);
676 p++;
677 if (err_occurred())
678 return NULL;
679 }
680
681 INCREF(None);
682 return None;
683}
684
685static object *
686CD_removecallback(self, args)
687 cdparserobject *self;
688 object *args;
689{
690 int type;
691
692 CheckParser(self);
693
694 if (!getargs(args, "i", &type))
695 return NULL;
696
697 CDremovecallback(self->ob_cdparser, (CDDATATYPES) type);
698
699 XDECREF(self->ob_cdcallbacks[type].ob_cdcallback);
700 self->ob_cdcallbacks[type].ob_cdcallback = NULL;
701
702 XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
703 self->ob_cdcallbacks[type].ob_cdcallbackarg = NULL;
704
705 INCREF(None);
706 return None;
707}
708
709static object *
710CD_resetparser(self, args)
711 cdparserobject *self;
712 object *args;
713{
714 CheckParser(self);
715
716 if (!getnoarg(args))
717 return NULL;
718
719 CDresetparser(self->ob_cdparser);
720
721 INCREF(None);
722 return None;
723}
724
725static object *
726CD_setcallback(self, args)
727 cdparserobject *self;
728 object *args;
729{
730 int type;
731 object *funcobject, *funcargobject;
732
733 CheckParser(self);
734
735 /* XXX - more work here */
736 if (!getargs(args, "(iOO)", &type, &funcobject, &funcargobject))
737 return NULL;
738
739 if (type < 0 || type >= NCALLBACKS) {
740 err_setstr(RuntimeError, "bad type");
741 return NULL;
742 }
743
744 CDsetcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback, (void *) self);
745 XDECREF(self->ob_cdcallbacks[type].ob_cdcallback);
746 INCREF(funcobject);
747 self->ob_cdcallbacks[type].ob_cdcallback = funcobject;
748 XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
749 INCREF(funcargobject);
750 self->ob_cdcallbacks[type].ob_cdcallbackarg = funcargobject;
751
752 INCREF(None);
753 return None;
754}
755
756static struct methodlist cdparser_methods[] = {
757 {"deleteparser", CD_deleteparser},
758 {"parseframe", CD_parseframe},
759 {"removecallback", CD_removecallback},
760 {"resetparser", CD_resetparser},
761 {"setcallback", CD_setcallback},
762 {NULL, NULL} /* sentinel */
763};
764
765static void
766cdparser_dealloc(self)
767 cdparserobject *self;
768{
769 int i;
770
771 for (i = 0; i < NCALLBACKS; i++) {
772 XDECREF(self->ob_cdcallbacks[i].ob_cdcallback);
773 self->ob_cdcallbacks[i].ob_cdcallback = NULL;
774 XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg);
775 self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
776 }
777 CDdeleteparser(self->ob_cdparser);
778 DEL(self);
779}
780
781static object *
782cdparser_getattr(cdp, name)
783 cdparserobject *cdp;
784 char *name;
785{
786 return findmethod(cdparser_methods, (object *)cdp, name);
787}
788
789typeobject CdParsertype = {
790 OB_HEAD_INIT(&Typetype)
791 0, /*ob_size*/
792 "cdparser", /*tp_name*/
793 sizeof(cdparserobject), /*tp_size*/
794 0, /*tp_itemsize*/
795 /* methods */
796 cdparser_dealloc, /*tp_dealloc*/
797 0, /*tp_print*/
798 cdparser_getattr, /*tp_getattr*/
799 0, /*tp_setattr*/
800 0, /*tp_compare*/
801 0, /*tp_repr*/
802};
803
804static object *
805newcdparserobject(cdp)
806 CDPARSER *cdp;
807{
808 cdparserobject *p;
809 int i;
810
811 p = NEWOBJ(cdparserobject, &CdParsertype);
812 if (p == NULL)
813 return NULL;
814 p->ob_cdparser = cdp;
815 for (i = 0; i < NCALLBACKS; i++) {
816 p->ob_cdcallbacks[i].ob_cdcallback = NULL;
817 p->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
818 }
819 return (object *) p;
820}
821
822static object *
823CD_createparser(self, args)
824 object *self, *args;
825{
826 CDPARSER *cdp;
827
828 if (!getnoarg(args))
829 return NULL;
830 cdp = CDcreateparser();
831 if (cdp == NULL) {
832 err_setstr(RuntimeError, "createparser failed");
833 return NULL;
834 }
835
836 return newcdparserobject(cdp);
837}
838
839static object *
840CD_sbtoa(self, args)
841 object *self;
842 object *args;
843{
844 char *sb;
845 int length;
846 object *result;
847
848 if (!getargs(args, "s#", &sb, &length))
849 return NULL;
850 result = newsizedstringobject(NULL, length);
851 CDsbtoa(getstringvalue(result), (unchar *) sb, length);
852 return result;
853}
854
855static object *
856CD_timetoa(self, args)
857 object *self;
858 object *args;
859{
860 char *tc;
861 int length;
862 object *result;
863
864 if (!getargs(args, "s#", &tc, &length))
865 return NULL;
866
867 if (length != sizeof(struct cdtimecode)) {
868 err_setstr(RuntimeError, "bad length");
869 return NULL;
870 }
871
872 result = newsizedstringobject(NULL, 8);
873 CDtimetoa(getstringvalue(result), (struct cdtimecode *) tc);
874 return result;
875}
876
877static struct methodlist CD_methods[] = {
878 {"sbtoa", CD_sbtoa},
879 {"open", CD_open},
880 {"createparser",CD_createparser},
881 {"timetoa", CD_timetoa},
882 {NULL, NULL} /* Sentinel */
883};
884
885void
886initcd()
887{
888 (void) initmodule("cd", CD_methods);
889}