RE: How to load a "loadable object" that has a non-default file extension ?
[perl.git] / win32 / win32sck.c
1 /* win32sck.c
2  *
3  * (c) 1995 Microsoft Corporation. All rights reserved. 
4  *              Developed by hip communications inc., http://info.hip.com/info/
5  * Portions (c) 1993 Intergraph Corporation. All rights reserved.
6  *
7  *    You may distribute under the terms of either the GNU General Public
8  *    License or the Artistic License, as specified in the README file.
9  */
10
11 #define WIN32IO_IS_STDIO
12 #define WIN32SCK_IS_STDSCK
13 #define WIN32_LEAN_AND_MEAN
14 #define PERLIO_NOT_STDIO 0
15 #ifdef __GNUC__
16 #define Win32_Winsock
17 #endif
18 #include <windows.h>
19 #include <ws2spi.h>
20
21 #include "EXTERN.h"
22 #include "perl.h"
23
24 #include "Win32iop.h"
25 #include <sys/socket.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <assert.h>
29 #include <io.h>
30
31 /* thanks to Beverly Brown      (beverly@datacube.com) */
32 #ifdef USE_SOCKETS_AS_HANDLES
33 #       define OPEN_SOCKET(x)   win32_open_osfhandle(x,O_RDWR|O_BINARY)
34 #       define TO_SOCKET(x)     _get_osfhandle(x)
35 #else
36 #       define OPEN_SOCKET(x)   (x)
37 #       define TO_SOCKET(x)     (x)
38 #endif  /* USE_SOCKETS_AS_HANDLES */
39
40 #define StartSockets() \
41     STMT_START {                                        \
42         if (!wsock_started)                             \
43             start_sockets();                            \
44     } STMT_END
45
46 #define SOCKET_TEST(x, y) \
47     STMT_START {                                        \
48         StartSockets();                                 \
49         if((x) == (y))                                  \
50             errno = WSAGetLastError();                  \
51     } STMT_END
52
53 #define SOCKET_TEST_ERROR(x) SOCKET_TEST(x, SOCKET_ERROR)
54
55 static struct servent* win32_savecopyservent(struct servent*d,
56                                              struct servent*s,
57                                              const char *proto);
58
59 static int wsock_started = 0;
60
61 EXTERN_C void
62 EndSockets(void)
63 {
64     if (wsock_started)
65         WSACleanup();
66 }
67
68 void
69 start_sockets(void) 
70 {
71     dTHX;
72     unsigned short version;
73     WSADATA retdata;
74     int ret;
75
76     /*
77      * initalize the winsock interface and insure that it is
78      * cleaned up at exit.
79      */
80     version = 0x2;
81     if(ret = WSAStartup(version, &retdata))
82         Perl_croak_nocontext("Unable to locate winsock library!\n");
83     if(retdata.wVersion != version)
84         Perl_croak_nocontext("Could not find version 2.0 of winsock dll\n");
85
86     /* atexit((void (*)(void)) EndSockets); */
87     wsock_started = 1;
88 }
89
90 #ifndef USE_SOCKETS_AS_HANDLES
91 #undef fdopen
92 FILE *
93 my_fdopen(int fd, char *mode)
94 {
95     FILE *fp;
96     char sockbuf[256];
97     int optlen = sizeof(sockbuf);
98     int retval;
99
100     if (!wsock_started)
101         return(fdopen(fd, mode));
102
103     retval = getsockopt((SOCKET)fd, SOL_SOCKET, SO_TYPE, sockbuf, &optlen);
104     if(retval == SOCKET_ERROR && WSAGetLastError() == WSAENOTSOCK) {
105         return(fdopen(fd, mode));
106     }
107
108     /*
109      * If we get here, then fd is actually a socket.
110      */
111     Newxz(fp, 1, FILE); /* XXX leak, good thing this code isn't used */
112     if(fp == NULL) {
113         errno = ENOMEM;
114         return NULL;
115     }
116
117     fp->_file = fd;
118     if(*mode == 'r')
119         fp->_flag = _IOREAD;
120     else
121         fp->_flag = _IOWRT;
122    
123     return fp;
124 }
125 #endif  /* USE_SOCKETS_AS_HANDLES */
126
127
128 u_long
129 win32_htonl(u_long hostlong)
130 {
131     StartSockets();
132     return htonl(hostlong);
133 }
134
135 u_short
136 win32_htons(u_short hostshort)
137 {
138     StartSockets();
139     return htons(hostshort);
140 }
141
142 u_long
143 win32_ntohl(u_long netlong)
144 {
145     StartSockets();
146     return ntohl(netlong);
147 }
148
149 u_short
150 win32_ntohs(u_short netshort)
151 {
152     StartSockets();
153     return ntohs(netshort);
154 }
155
156
157
158 SOCKET
159 win32_accept(SOCKET s, struct sockaddr *addr, int *addrlen)
160 {
161     SOCKET r;
162
163     SOCKET_TEST((r = accept(TO_SOCKET(s), addr, addrlen)), INVALID_SOCKET);
164     return OPEN_SOCKET(r);
165 }
166
167 int
168 win32_bind(SOCKET s, const struct sockaddr *addr, int addrlen)
169 {
170     int r;
171
172     SOCKET_TEST_ERROR(r = bind(TO_SOCKET(s), addr, addrlen));
173     return r;
174 }
175
176 int
177 win32_connect(SOCKET s, const struct sockaddr *addr, int addrlen)
178 {
179     int r;
180
181     SOCKET_TEST_ERROR(r = connect(TO_SOCKET(s), addr, addrlen));
182     return r;
183 }
184
185
186 int
187 win32_getpeername(SOCKET s, struct sockaddr *addr, int *addrlen)
188 {
189     int r;
190
191     SOCKET_TEST_ERROR(r = getpeername(TO_SOCKET(s), addr, addrlen));
192     return r;
193 }
194
195 int
196 win32_getsockname(SOCKET s, struct sockaddr *addr, int *addrlen)
197 {
198     int r;
199
200     SOCKET_TEST_ERROR(r = getsockname(TO_SOCKET(s), addr, addrlen));
201     return r;
202 }
203
204 int
205 win32_getsockopt(SOCKET s, int level, int optname, char *optval, int *optlen)
206 {
207     int r;
208
209     SOCKET_TEST_ERROR(r = getsockopt(TO_SOCKET(s), level, optname, optval, optlen));
210     return r;
211 }
212
213 int
214 win32_ioctlsocket(SOCKET s, long cmd, u_long *argp)
215 {
216     int r;
217
218     SOCKET_TEST_ERROR(r = ioctlsocket(TO_SOCKET(s), cmd, argp));
219     return r;
220 }
221
222 int
223 win32_listen(SOCKET s, int backlog)
224 {
225     int r;
226
227     SOCKET_TEST_ERROR(r = listen(TO_SOCKET(s), backlog));
228     return r;
229 }
230
231 int
232 win32_recv(SOCKET s, char *buf, int len, int flags)
233 {
234     int r;
235
236     SOCKET_TEST_ERROR(r = recv(TO_SOCKET(s), buf, len, flags));
237     return r;
238 }
239
240 int
241 win32_recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
242 {
243     int r;
244     int frombufsize = *fromlen;
245
246     SOCKET_TEST_ERROR(r = recvfrom(TO_SOCKET(s), buf, len, flags, from, fromlen));
247     /* Winsock's recvfrom() only returns a valid 'from' when the socket
248      * is connectionless.  Perl expects a valid 'from' for all types
249      * of sockets, so go the extra mile.
250      */
251     if (r != SOCKET_ERROR && frombufsize == *fromlen)
252         (void)win32_getpeername(s, from, fromlen);
253     return r;
254 }
255
256 /* select contributed by Vincent R. Slyngstad (vrs@ibeam.intel.com) */
257 int
258 win32_select(int nfds, Perl_fd_set* rd, Perl_fd_set* wr, Perl_fd_set* ex, const struct timeval* timeout)
259 {
260     int r;
261 #ifdef USE_SOCKETS_AS_HANDLES
262     Perl_fd_set dummy;
263     int i, fd, save_errno = errno;
264     FD_SET nrd, nwr, nex, *prd, *pwr, *pex;
265
266     /* winsock seems incapable of dealing with all three null fd_sets,
267      * so do the (millisecond) sleep as a special case
268      */
269     if (!(rd || wr || ex)) {
270         if (timeout)
271             Sleep(timeout->tv_sec  * 1000 +
272                   timeout->tv_usec / 1000);     /* do the best we can */
273         else
274             Sleep(UINT_MAX);
275         return 0;
276     }
277     StartSockets();
278     PERL_FD_ZERO(&dummy);
279     if (!rd)
280         rd = &dummy, prd = NULL;
281     else
282         prd = &nrd;
283     if (!wr)
284         wr = &dummy, pwr = NULL;
285     else
286         pwr = &nwr;
287     if (!ex)
288         ex = &dummy, pex = NULL;
289     else
290         pex = &nex;
291
292     FD_ZERO(&nrd);
293     FD_ZERO(&nwr);
294     FD_ZERO(&nex);
295     for (i = 0; i < nfds; i++) {
296         fd = TO_SOCKET(i);
297         if (PERL_FD_ISSET(i,rd))
298             FD_SET((unsigned)fd, &nrd);
299         if (PERL_FD_ISSET(i,wr))
300             FD_SET((unsigned)fd, &nwr);
301         if (PERL_FD_ISSET(i,ex))
302             FD_SET((unsigned)fd, &nex);
303     }
304
305     errno = save_errno;
306     SOCKET_TEST_ERROR(r = select(nfds, prd, pwr, pex, timeout));
307     save_errno = errno;
308
309     for (i = 0; i < nfds; i++) {
310         fd = TO_SOCKET(i);
311         if (PERL_FD_ISSET(i,rd) && !FD_ISSET(fd, &nrd))
312             PERL_FD_CLR(i,rd);
313         if (PERL_FD_ISSET(i,wr) && !FD_ISSET(fd, &nwr))
314             PERL_FD_CLR(i,wr);
315         if (PERL_FD_ISSET(i,ex) && !FD_ISSET(fd, &nex))
316             PERL_FD_CLR(i,ex);
317     }
318     errno = save_errno;
319 #else
320     SOCKET_TEST_ERROR(r = select(nfds, rd, wr, ex, timeout));
321 #endif
322     return r;
323 }
324
325 int
326 win32_send(SOCKET s, const char *buf, int len, int flags)
327 {
328     int r;
329
330     SOCKET_TEST_ERROR(r = send(TO_SOCKET(s), buf, len, flags));
331     return r;
332 }
333
334 int
335 win32_sendto(SOCKET s, const char *buf, int len, int flags,
336              const struct sockaddr *to, int tolen)
337 {
338     int r;
339
340     SOCKET_TEST_ERROR(r = sendto(TO_SOCKET(s), buf, len, flags, to, tolen));
341     return r;
342 }
343
344 int
345 win32_setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen)
346 {
347     int r;
348
349     SOCKET_TEST_ERROR(r = setsockopt(TO_SOCKET(s), level, optname, optval, optlen));
350     return r;
351 }
352     
353 int
354 win32_shutdown(SOCKET s, int how)
355 {
356     int r;
357
358     SOCKET_TEST_ERROR(r = shutdown(TO_SOCKET(s), how));
359     return r;
360 }
361
362 int
363 win32_closesocket(SOCKET s)
364 {
365     int r;
366
367     SOCKET_TEST_ERROR(r = closesocket(TO_SOCKET(s)));
368     return r;
369 }
370
371 #ifdef USE_SOCKETS_AS_HANDLES
372 #define WIN32_OPEN_SOCKET(af, type, protocol) open_ifs_socket(af, type, protocol)
373
374 void
375 convert_proto_info_w2a(WSAPROTOCOL_INFOW *in, WSAPROTOCOL_INFOA *out)
376 {
377     Copy(in, out, 1, WSAPROTOCOL_INFOA);
378     wcstombs(out->szProtocol, in->szProtocol, sizeof(out->szProtocol));
379 }
380
381 SOCKET
382 open_ifs_socket(int af, int type, int protocol)
383 {
384     dTHX;
385     char *s;
386     unsigned long proto_buffers_len = 0;
387     int error_code;
388     SOCKET out = INVALID_SOCKET;
389
390     if ((s = PerlEnv_getenv("PERL_ALLOW_NON_IFS_LSP")) && atoi(s))
391         return WSASocket(af, type, protocol, NULL, 0, 0);
392
393     if (WSCEnumProtocols(NULL, NULL, &proto_buffers_len, &error_code) == SOCKET_ERROR
394         && error_code == WSAENOBUFS)
395     {
396         WSAPROTOCOL_INFOW *proto_buffers;
397         int protocols_available = 0;       
398  
399         Newx(proto_buffers, proto_buffers_len / sizeof(WSAPROTOCOL_INFOW),
400             WSAPROTOCOL_INFOW);
401
402         if ((protocols_available = WSCEnumProtocols(NULL, proto_buffers, 
403             &proto_buffers_len, &error_code)) != SOCKET_ERROR)
404         {
405             int i;
406             for (i = 0; i < protocols_available; i++)
407             {
408                 WSAPROTOCOL_INFOA proto_info;
409
410                 if ((af != AF_UNSPEC && af != proto_buffers[i].iAddressFamily)
411                     || (type != proto_buffers[i].iSocketType)
412                     || (protocol != 0 && proto_buffers[i].iProtocol != 0 &&
413                         protocol != proto_buffers[i].iProtocol))
414                     continue;
415
416                 if ((proto_buffers[i].dwServiceFlags1 & XP1_IFS_HANDLES) == 0)
417                     continue;
418
419                 convert_proto_info_w2a(&(proto_buffers[i]), &proto_info);
420
421                 out = WSASocket(af, type, protocol, &proto_info, 0, 0);
422                 break;
423             }
424         }
425
426         Safefree(proto_buffers);
427     }
428
429     return out;
430 }
431
432 #else
433 #define WIN32_OPEN_SOCKET(af, type, protocol) socket(af, type, protocol)
434 #endif
435
436 SOCKET
437 win32_socket(int af, int type, int protocol)
438 {
439     SOCKET s;
440
441 #ifndef USE_SOCKETS_AS_HANDLES
442     SOCKET_TEST(s = socket(af, type, protocol), INVALID_SOCKET);
443 #else
444     StartSockets();
445
446     if((s = WIN32_OPEN_SOCKET(af, type, protocol)) == INVALID_SOCKET)
447         errno = WSAGetLastError();
448     else
449         s = OPEN_SOCKET(s);
450 #endif  /* USE_SOCKETS_AS_HANDLES */
451
452     return s;
453 }
454
455 /*
456  * close RTL fd while respecting sockets
457  * added as temporary measure until PerlIO has real
458  * Win32 native layer
459  *   -- BKS, 11-11-2000
460 */
461
462 int my_close(int fd)
463 {
464     int osf;
465     if (!wsock_started)         /* No WinSock? */
466         return(close(fd));      /* Then not a socket. */
467     osf = TO_SOCKET(fd);/* Get it now before it's gone! */
468     if (osf != -1) {
469         int err;
470         err = closesocket(osf);
471         if (err == 0) {
472 #if defined(USE_FIXED_OSFHANDLE) || defined(PERL_MSVCRT_READFIX)
473             _set_osfhnd(fd, INVALID_HANDLE_VALUE);
474 #endif
475             (void)close(fd);    /* handle already closed, ignore error */
476             return 0;
477         }
478         else if (err == SOCKET_ERROR) {
479             err = WSAGetLastError();
480             if (err != WSAENOTSOCK) {
481                 (void)close(fd);
482                 errno = err;
483                 return EOF;
484             }
485         }
486     }
487     return close(fd);
488 }
489
490 #undef fclose
491 int
492 my_fclose (FILE *pf)
493 {
494     int osf;
495     if (!wsock_started)         /* No WinSock? */
496         return(fclose(pf));     /* Then not a socket. */
497     osf = TO_SOCKET(win32_fileno(pf));/* Get it now before it's gone! */
498     if (osf != -1) {
499         int err;
500         win32_fflush(pf);
501         err = closesocket(osf);
502         if (err == 0) {
503 #if defined(USE_FIXED_OSFHANDLE) || defined(PERL_MSVCRT_READFIX)
504             _set_osfhnd(win32_fileno(pf), INVALID_HANDLE_VALUE);
505 #endif
506             (void)fclose(pf);   /* handle already closed, ignore error */
507             return 0;
508         }
509         else if (err == SOCKET_ERROR) {
510             err = WSAGetLastError();
511             if (err != WSAENOTSOCK) {
512                 (void)fclose(pf);
513                 errno = err;
514                 return EOF;
515             }
516         }
517     }
518     return fclose(pf);
519 }
520
521 #undef fstat
522 int
523 my_fstat(int fd, Stat_t *sbufptr)
524 {
525     /* This fixes a bug in fstat() on Windows 9x.  fstat() uses the
526      * GetFileType() win32 syscall, which will fail on Windows 9x.
527      * So if we recognize a socket on Windows 9x, we return the
528      * same results as on Windows NT/2000.
529      * XXX this should be extended further to set S_IFSOCK on
530      * sbufptr->st_mode.
531      */
532     int osf;
533     if (!wsock_started || IsWinNT()) {
534 #if defined(WIN64) || defined(USE_LARGE_FILES)
535 #if defined(__BORLANDC__) /* buk */
536         return win32_fstat(fd, sbufptr );
537 #else
538         return _fstati64(fd, sbufptr);
539 #endif
540 #else
541         return fstat(fd, sbufptr);
542 #endif
543     }
544
545     osf = TO_SOCKET(fd);
546     if (osf != -1) {
547         char sockbuf[256];
548         int optlen = sizeof(sockbuf);
549         int retval;
550
551         retval = getsockopt((SOCKET)osf, SOL_SOCKET, SO_TYPE, sockbuf, &optlen);
552         if (retval != SOCKET_ERROR || WSAGetLastError() != WSAENOTSOCK) {
553 #if defined(__BORLANDC__)&&(__BORLANDC__<=0x520)
554             sbufptr->st_mode = S_IFIFO;
555 #else
556             sbufptr->st_mode = _S_IFIFO;
557 #endif
558             sbufptr->st_rdev = sbufptr->st_dev = (dev_t)fd;
559             sbufptr->st_nlink = 1;
560             sbufptr->st_uid = sbufptr->st_gid = sbufptr->st_ino = 0;
561             sbufptr->st_atime = sbufptr->st_mtime = sbufptr->st_ctime = 0;
562             sbufptr->st_size = (Off_t)0;
563             return 0;
564         }
565     }
566 #if defined(WIN64) || defined(USE_LARGE_FILES)
567 #if defined(__BORLANDC__) /* buk */
568     return win32_fstat(fd, sbufptr );
569 #else
570     return _fstati64(fd, sbufptr);
571 #endif
572 #else
573     return fstat(fd, sbufptr);
574 #endif
575 }
576
577 struct hostent *
578 win32_gethostbyaddr(const char *addr, int len, int type)
579 {
580     struct hostent *r;
581
582     SOCKET_TEST(r = gethostbyaddr(addr, len, type), NULL);
583     return r;
584 }
585
586 struct hostent *
587 win32_gethostbyname(const char *name)
588 {
589     struct hostent *r;
590
591     SOCKET_TEST(r = gethostbyname(name), NULL);
592     return r;
593 }
594
595 int
596 win32_gethostname(char *name, int len)
597 {
598     int r;
599
600     SOCKET_TEST_ERROR(r = gethostname(name, len));
601     return r;
602 }
603
604 struct protoent *
605 win32_getprotobyname(const char *name)
606 {
607     struct protoent *r;
608
609     SOCKET_TEST(r = getprotobyname(name), NULL);
610     return r;
611 }
612
613 struct protoent *
614 win32_getprotobynumber(int num)
615 {
616     struct protoent *r;
617
618     SOCKET_TEST(r = getprotobynumber(num), NULL);
619     return r;
620 }
621
622 struct servent *
623 win32_getservbyname(const char *name, const char *proto)
624 {
625     dTHX;    
626     struct servent *r;
627
628     SOCKET_TEST(r = getservbyname(name, proto), NULL);
629     if (r) {
630         r = win32_savecopyservent(&w32_servent, r, proto);
631     }
632     return r;
633 }
634
635 struct servent *
636 win32_getservbyport(int port, const char *proto)
637 {
638     dTHX; 
639     struct servent *r;
640
641     SOCKET_TEST(r = getservbyport(port, proto), NULL);
642     if (r) {
643         r = win32_savecopyservent(&w32_servent, r, proto);
644     }
645     return r;
646 }
647
648 int
649 win32_ioctl(int i, unsigned int u, char *data)
650 {
651     dTHX;
652     u_long u_long_arg; 
653     int retval;
654     
655     if (!wsock_started) {
656         Perl_croak_nocontext("ioctl implemented only on sockets");
657         /* NOTREACHED */
658     }
659
660     /* mauke says using memcpy avoids alignment issues */
661     memcpy(&u_long_arg, data, sizeof u_long_arg); 
662     retval = ioctlsocket(TO_SOCKET(i), (long)u, &u_long_arg);
663     memcpy(data, &u_long_arg, sizeof u_long_arg);
664     
665     if (retval == SOCKET_ERROR) {
666         if (WSAGetLastError() == WSAENOTSOCK) {
667             Perl_croak_nocontext("ioctl implemented only on sockets");
668             /* NOTREACHED */
669         }
670         errno = WSAGetLastError();
671     }
672     return retval;
673 }
674
675 char FAR *
676 win32_inet_ntoa(struct in_addr in)
677 {
678     StartSockets();
679     return inet_ntoa(in);
680 }
681
682 unsigned long
683 win32_inet_addr(const char FAR *cp)
684 {
685     StartSockets();
686     return inet_addr(cp);
687 }
688
689 /*
690  * Networking stubs
691  */
692
693 void
694 win32_endhostent() 
695 {
696     dTHX;
697     Perl_croak_nocontext("endhostent not implemented!\n");
698 }
699
700 void
701 win32_endnetent()
702 {
703     dTHX;
704     Perl_croak_nocontext("endnetent not implemented!\n");
705 }
706
707 void
708 win32_endprotoent()
709 {
710     dTHX;
711     Perl_croak_nocontext("endprotoent not implemented!\n");
712 }
713
714 void
715 win32_endservent()
716 {
717     dTHX;
718     Perl_croak_nocontext("endservent not implemented!\n");
719 }
720
721
722 struct netent *
723 win32_getnetent(void) 
724 {
725     dTHX;
726     Perl_croak_nocontext("getnetent not implemented!\n");
727     return (struct netent *) NULL;
728 }
729
730 struct netent *
731 win32_getnetbyname(char *name) 
732 {
733     dTHX;
734     Perl_croak_nocontext("getnetbyname not implemented!\n");
735     return (struct netent *)NULL;
736 }
737
738 struct netent *
739 win32_getnetbyaddr(long net, int type) 
740 {
741     dTHX;
742     Perl_croak_nocontext("getnetbyaddr not implemented!\n");
743     return (struct netent *)NULL;
744 }
745
746 struct protoent *
747 win32_getprotoent(void) 
748 {
749     dTHX;
750     Perl_croak_nocontext("getprotoent not implemented!\n");
751     return (struct protoent *) NULL;
752 }
753
754 struct servent *
755 win32_getservent(void) 
756 {
757     dTHX;
758     Perl_croak_nocontext("getservent not implemented!\n");
759     return (struct servent *) NULL;
760 }
761
762 void
763 win32_sethostent(int stayopen)
764 {
765     dTHX;
766     Perl_croak_nocontext("sethostent not implemented!\n");
767 }
768
769
770 void
771 win32_setnetent(int stayopen)
772 {
773     dTHX;
774     Perl_croak_nocontext("setnetent not implemented!\n");
775 }
776
777
778 void
779 win32_setprotoent(int stayopen)
780 {
781     dTHX;
782     Perl_croak_nocontext("setprotoent not implemented!\n");
783 }
784
785
786 void
787 win32_setservent(int stayopen)
788 {
789     dTHX;
790     Perl_croak_nocontext("setservent not implemented!\n");
791 }
792
793 static struct servent*
794 win32_savecopyservent(struct servent*d, struct servent*s, const char *proto)
795 {
796     d->s_name = s->s_name;
797     d->s_aliases = s->s_aliases;
798     d->s_port = s->s_port;
799 #ifndef __BORLANDC__    /* Buggy on Win95 and WinNT-with-Borland-WSOCK */
800     if (!IsWin95() && s->s_proto && strlen(s->s_proto))
801         d->s_proto = s->s_proto;
802     else
803 #endif
804     if (proto && strlen(proto))
805         d->s_proto = (char *)proto;
806     else
807         d->s_proto = "tcp";
808    
809     return d;
810 }
811
812