Add keys(@a) and values(@a) to perldelta
[perl.git] / mpeix / mpeix.c
1
2 /*
3  * gcc long pointer support code for HPPA.
4  * Copyright 1998, DIS International, Ltd.
5  * This code is free software; you may redistribute it and/or modify
6  * it under the same terms as Perl itself.  (Relicensed for Perl in
7  * in April 2002 by Mark Klein.)
8  */
9 typedef struct {
10   int           spaceid;
11   unsigned int  offset;
12   } LONGPOINTER, longpointer;
13
14 /*
15  * gcc long pointer support code for HPPA.
16  * Copyright 1998, DIS International, Ltd.
17  * This code is free software; you may redistribute it and/or modify
18  * it under the same terms as Perl itself.  (Relicensed for Perl in
19  * in April 2002 by Mark Klein.)
20  */
21
22 int __perl_mpe_getspaceid(void *source)
23   {
24   int val;
25   /*
26    * Given the short pointer, determine it's space ID.
27    */
28
29   /*
30    * The colons separate output from input parameters. In this case,
31    * the output of the instruction (output indicated by the "=" in the
32    * constraint) is to a memory location (indicated by the "m"). The
33    * input constraint indicates that the source to the instruction
34    * is a register reference (indicated by the "r").
35    * The general format is:
36    *   asm("<instruction template>" : <output> : <input> : <clobbers>);
37    *     where <output> and <input> are:
38    *       "<constraint>" (<token>)
39    *     <instruction template> is the PA-RISC instruction in template fmt.
40    *     <clobbers> indicates those registers clobbered by the instruction
41    *     and provides hints to the optimizer.
42    *
43    * Refer to the gcc documentation
44    */
45   __asm__ __volatile__ (
46       "   comiclr,= 0,%1,%%r28\n"
47       "\t  ldsid (%%r0,%1),%%r28\n"
48       "\t stw %%r28, %0"
49                         : "=m" (val)    // Output to val
50                         : "r" (source)  // Source must be gen reg
51                         : "%r28");      // Clobbers %r28
52   return (val);
53   };
54
55 LONGPOINTER __perl_mpe_longaddr(void *source)
56   {
57   LONGPOINTER lptr;
58   /*
59    * Return the long pointer for the address in sr5 space.
60    */
61
62   __asm__ __volatile__ (
63       "  comiclr,= 0,%2,%%r28\n"
64       "\t    ldsid (%%r0,%2),%%r28\n"
65       "\t  stw %%r28, %0\n"
66       "\t  stw %2, %1"
67                         : "=m" (lptr.spaceid),
68                           "=m" (lptr.offset)    // Store to lptr
69                         : "r" (source)          // Source must be gen reg
70                         : "%r28");      // Clobbers %r28
71   return (lptr);
72   };
73
74 LONGPOINTER __perl_mpe_addtopointer(LONGPOINTER source,    // %r26 == source offset
75                                                 // %r25 == source space
76                         int             len)    // %r24 == length in bytes
77   {
78   /*
79    * Increment a longpointer.
80    */
81
82   __asm__ __volatile__ (
83       "  copy %0,%%r28\n"                       // copy space to r28
84       "\t  add %1,%2,%%r29"                     // Increment the pointer
85                         :                       // No output
86                         : "r" (source.spaceid), // Source address
87                           "r" (source.offset),
88                           "r" (len)             // Length
89                         : "%r28",               // Clobbers
90                           "%r29");
91   };
92
93 void __perl_mpe_longmove(int len,                  // %r26 == byte length
94               LONGPOINTER source,       // %r23 == source space, %r24 == off
95               LONGPOINTER target)       // sp-#56 == target space, sp-#52== off
96   {
97   /*
98    * Move data between two buffers in long pointer space.
99    */
100
101   __asm__ __volatile__ (
102       "  .import $$lr_unk_unk_long,MILLICODE\n"
103       "\t  mtsp %0,%%sr1\n"                     // copy source space to sr1
104       "\t  copy %1,%%r26\n"                     // load source offset to r26
105       "\t  copy %4,%%r24\n"                     // load length to r24
106       "\t  copy %3,%%r25\n"                     // load target offset to r25
107       "\t  bl $$lr_unk_unk_long,%%r31\n"        // start branch to millicode
108       "\t  mtsp %2,%%sr2"                       // copy target space to sr2
109                         :                       // No output
110                         : "r" (source.spaceid), // Source address
111                           "r" (source.offset),
112                           "r" (target.spaceid), // Target address
113                           "r" (target.offset),
114                           "r" (len)             // Byte length
115                         : "%r1",                // Clobbers
116                           "%r24",
117                           "%r25",
118                           "%r26",
119                           "%r31");
120   };
121
122 int __perl_mpe_longpeek(LONGPOINTER source)
123   {
124   /*
125    * Fetch the int in long pointer space.
126    */
127   unsigned int val;
128
129   __asm__ __volatile__ (
130       "  mtsp %1, %%sr1\n"
131       "\t  copy %2, %%r28\n"
132       "\t  ldw 0(%%sr1, %%r28), %%r28\n"
133       "\t  stw %%r28, %0"
134                         : "=m" (val)            // Output val
135                         : "r" (source.spaceid), // Source space ID
136                           "r" (source.offset)   // Source offset
137                         : "%r28");              // Clobbers %r28
138
139   return (val);
140   };
141
142 void __perl_mpe_longpoke(LONGPOINTER target,       // %r25 == spaceid, %r26 == offset
143           unsigned int val)             // %r24 == value
144   {
145   /*
146    * Store the val into long pointer space.
147    */
148   __asm__ __volatile__ (
149       "  mtsp %0,%%sr1\n"
150       "\t  copy %1, %%r28\n"
151       "\t  stw %2, 0(%%sr1, %%r28)"
152                         :                       // No output
153                         : "r" (target.spaceid), // Target space ID
154                           "r" (target.offset),  // Target offset
155                           "r" (val)             // Value to store
156                         : "%r28"                // Clobbers %r28
157                         );                      // Copy space to %sr1
158   };
159
160 void __perl_mpe_move_fast(int len,                 // %r26 == byte length
161                void *source,            // %r25 == source addr
162                void *target)            // %r24 == target addr
163   {
164   /*
165    * Move using short pointers.
166    */
167   __asm__ __volatile__ (
168       "  .import $$lr_unk_unk,MILLICODE\n"
169       "\t  copy %1, %%r26\n"                    // Move source addr into pos
170       "\t  copy %2, %%r25\n"                    // Move target addr into pos
171       "\t  bl $$lr_unk_unk,%%r31\n"             // Start branch to millicode
172       "\t  copy %0, %%r24"                      // Move length into position
173                         :                       // No output
174                         : "r" (len),            // Byte length
175                           "r" (source),         // Source address
176                           "r" (target)          // Target address
177                         : "%r24",               // Clobbers
178                           "%r25",
179                           "%r26",
180                           "%r31");
181   };
182
183 /*
184  * ftruncate - set file size, BSD Style
185  *
186  * shortens or enlarges the file as neeeded
187  * uses some undocumented locking call. It is known to work on SCO unix,
188  * other vendors should try.
189  * The #error directive prevents unsupported OSes
190  *
191  * ftruncate/truncate code by Mark Bixby.
192  * This code is free software; you may redistribute it and/or modify
193  * it under the same terms as Perl itself.
194  *
195  */
196
197 #ifndef _POSIX_SOURCE
198 #  define _POSIX_SOURCE
199 #endif
200 #ifndef _SOCKET_SOURCE
201 #  define _SOCKET_SOURCE
202 #endif
203 #include <unistd.h>
204 #include <errno.h>
205 #include <fcntl.h>
206 #include <stdio.h>
207 #include <string.h>
208 #include <sys/socket.h>
209 #include <limits.h>
210 #include <mpe.h>
211
212 extern void FCONTROL(short, short, longpointer);
213 extern void PRINTFILEINFO(int);
214
215 int ftruncate(int fd, long wantsize);
216
217 int
218 ftruncate(int fd, long wantsize)
219 {
220   int ccode_return,dummy=0;
221
222   if (lseek(fd, wantsize, SEEK_SET) < 0)
223   {
224       return (-1);
225   }
226
227   FCONTROL(_mpe_fileno(fd),6,__perl_mpe_longaddr(&dummy)); /* Write new EOF */
228   if ((ccode_return=ccode()) != CCE)
229   {
230           fprintf(stderr,
231               "MPE ftruncate failed, ccode=%d, wantsize=%ld\n",
232               ccode_return, wantsize);
233           PRINTFILEINFO(_mpe_fileno(fd));
234           errno = ESYSERR;
235           return (-1);
236   }
237
238   return (0);
239 }
240
241 /*
242    wrapper for truncate():
243
244    truncate() is UNIX, not POSIX.
245
246    This function requires ftruncate().
247
248
249
250    NAME
251       truncate -
252
253    SYNOPSIS
254       #include <unistd.h>
255
256       int truncate(const char *pathname, off_t length);
257
258                                              Returns: 0 if OK, -1 on error
259
260             from: Stevens' Advanced Programming in the UNIX Environment, p. 92
261
262
263
264    ERRORS
265       EACCES
266       EBADF
267       EDQUOT (not POSIX)    <- not implemented here
268       EFAULT
269       EINVAL
270       EISDIR
271       ELOOP (not POSIX)     <- not implemented here
272       ENAMETOOLONG
273       ENOTDIR
274       EROFS
275       ETXTBSY (not POSIX)   <- not implemented here
276
277                                           from: HP-UX man page
278
279
280
281    Compile directives:
282       PRINT_ERROR - make this function print an error message to stderr
283 */
284
285
286 #include <sys/types.h>  /* off_t, required by open() */
287 #include <sys/stat.h>   /* required by open() */
288 #include <fcntl.h>      /* open() */
289 #include <unistd.h>     /* close() */
290 #include <stdio.h>      /* perror(), sprintf() */
291
292
293
294 int
295 truncate(const char *pathname, off_t length)
296 {
297         int fd;
298 #ifdef PRINT_ERROR
299         char error_msg[80+1];
300 #endif
301
302         if (length == 0)
303         {
304                 if ( (fd = open(pathname, O_WRONLY | O_TRUNC)) < 0)
305                 {
306                         /* errno already set */
307 #ifdef PRINT_ERROR
308                         sprintf(error_msg,
309                                 "truncate(): open(%s, O_WRONLY | OTRUNC)\0",
310                                 pathname);
311                         perror(error_msg);
312 #endif
313                         return -1;
314                 }
315         }
316         else
317         {
318                 if ( (fd = open(pathname, O_WRONLY)) < 0)
319                 {
320                         /* errno already set */
321 #ifdef PRINT_ERROR
322                         sprintf(error_msg,
323                                 "truncate(): open(%s, O_WRONLY)\0",
324                                 pathname);
325                         perror(error_msg);
326 #endif
327                         return -1;
328                 }
329
330                 if (ftruncate(fd, length) < 0)
331                 {
332                         /* errno already set */
333 #ifdef PRINT_ERROR
334                         perror("truncate(): ftruncate()");
335 #endif
336                         return -1;
337                 }
338         }
339
340         if (close(fd) < 0)
341         {
342                 /* errno already set */
343 #ifdef PRINT_ERROR
344                 perror("truncate(): close()");
345 #endif
346                 return -1;
347         }
348
349         return 0;
350 } /* truncate() */
351
352 /* 
353    wrapper for gettimeofday():
354       gettimeofday() is UNIX, not POSIX.
355       gettimeofday() is a BSD function.
356
357    NAME
358       gettimeofday -
359
360    SYNOPSIS
361       #include <sys/time.h>
362
363       int gettimeofday(struct timeval *tp, struct timezone *tzp);
364
365    DESCRIPTION
366       This function returns seconds and microseconds since midnight
367       January 1, 1970. The microseconds is actually only accurate to
368       the millisecond.
369
370       Note: To pick up the definitions of structs timeval and timezone
371             from the <time.h> include file, the directive
372             _SOCKET_SOURCE must be used.
373
374    RETURN VALUE
375       A 0 return value indicates that the call succeeded.  A -1 return
376       value indicates an error occurred; errno is set to indicate the
377       error.
378
379    ERRORS
380       EFAULT     not implemented
381
382    Changes:
383       2-91    DR.  Created.
384 */
385
386
387 /* need _SOCKET_SOURCE to pick up structs timeval and timezone in time.h */
388 #ifndef _SOCKET_SOURCE
389 # define _SOCKET_SOURCE
390 #endif
391
392 #include <time.h>       /* structs timeval & timezone,
393                                 difftime(), localtime(), mktime(), time() */
394
395 extern int TIMER();
396
397 /*
398  * gettimeofday code by Mark Bixby.
399  * This code is free software; you may redistribute it and/or modify
400  * it under the same terms as Perl itself.
401  */
402
403 #ifdef __STDC__
404 int gettimeofday( struct timeval *tp, struct timezone *tpz )
405 #else
406 int gettimeofday(  tp, tpz )
407 struct timeval  *tp;
408 struct timezone *tpz;
409 #endif
410 {
411    static unsigned long    basetime        = 0;
412    static int              dsttime         = 0;
413    static int              minuteswest     = 0;
414    static int              oldtime         = 0;
415    register int            newtime;
416
417
418    /*-------------------------------------------------------------------*/
419    /* Setup a base from which all future time will be computed.         */
420    /*-------------------------------------------------------------------*/
421    if ( basetime == 0 )
422    {
423       time_t    gmt_time;
424       time_t    loc_time;
425       struct tm *loc_time_tm;
426
427       gmt_time    = time( NULL );
428       loc_time_tm = localtime( &gmt_time ) ;
429       loc_time    = mktime( loc_time_tm );
430
431       oldtime     = TIMER();
432       basetime    = (unsigned long) ( loc_time - (oldtime/1000) );
433
434       /*----------------------------------------------------------------*/
435       /* The calling process must be restarted if timezone or dst       */
436       /* changes.                                                       */
437       /*----------------------------------------------------------------*/
438       minuteswest = (int) (difftime( loc_time, gmt_time ) / 60);
439       dsttime     = loc_time_tm->tm_isdst;
440    }
441
442    /*-------------------------------------------------------------------*/
443    /* Get the new time value. The timer value rolls over every 24 days, */
444    /* so if the delta is negative, the basetime value is adjusted.      */
445    /*-------------------------------------------------------------------*/
446    newtime = TIMER();
447    if ( newtime < oldtime )  basetime += 2073600;
448    oldtime = newtime;
449
450    /*-------------------------------------------------------------------*/
451    /* Return the timestamp info.                                        */
452    /*-------------------------------------------------------------------*/
453    tp->tv_sec          = basetime + newtime/1000;
454    tp->tv_usec         = (newtime%1000) * 1000;   /* only accurate to milli */
455    if (tpz)
456    {
457       tpz->tz_minuteswest = minuteswest;
458       tpz->tz_dsttime     = dsttime;
459    }
460
461    return 0;
462
463 } /* gettimeofday() */
464
465 /*
466 **  MPE_FCNTL -- shadow function for fcntl()
467 **
468 **      MPE requires sfcntl() for sockets, and fcntl() for everything 
469 **      else.  This shadow routine determines the descriptor type and
470 **      makes the appropriate call.
471 **
472 **      Parameters:
473 **              same as fcntl().
474 **
475 **      Returns:
476 **              same as fcntl().
477 */
478
479 #include <stdarg.h>
480 #include <sys/socket.h>
481
482 int
483 mpe_fcntl(int fildes, int cmd, ...)
484 {
485         int len, result;
486         struct sockaddr sa;
487         
488         void *arg;
489         va_list ap;
490         
491         va_start(ap, cmd);
492         arg = va_arg(ap, void *);
493         va_end(ap);
494         
495         len = sizeof sa;
496         if (getsockname(fildes, &sa, &len) == -1)
497         {
498                 if (errno == EAFNOSUPPORT)
499                         /* AF_UNIX socket */
500                         return sfcntl(fildes, cmd, arg);
501
502                 if (errno == ENOTSOCK) 
503                         /* file or pipe */
504                         return fcntl(fildes, cmd, arg);
505
506                 /* unknown getsockname() failure */
507                 return (-1); 
508         }
509         else
510         {
511                 /* AF_INET socket */
512                 if ((result = sfcntl(fildes, cmd, arg)) != -1 && cmd == F_GETFL)
513                         result |= O_RDWR;  /* fill in some missing flags */
514                 return result;
515         }
516 }
517
518
519
520 /* 
521  * Stuff from here on down is written by Ken Hirsch
522  * and you may use it for any purpose.
523  * No warranty, express or implied.
524  */
525
526 #include <stddef.h>
527 #include <sys/ioctl.h>
528 #include <netinet/in.h>
529
530 #ifndef _SOCKLEN_T
531 typedef unsigned int socklen_t;
532 #define _SOCKLEN_T
533 #endif
534
535 static int max_io_size(int filedes);
536
537 ssize_t
538 mpe_read(int filedes, void *buffer, size_t len)
539 {
540   int maxio;
541   if (len > 4096 && (len > (maxio = max_io_size(filedes))))
542     len = maxio;
543
544   return read(filedes, buffer, len);
545 }
546
547 ssize_t
548 mpe_write(int filedes, const void *buffer, size_t len)
549 {
550   int written = 0;
551   int orig_len = len;
552   int maxio = (len>4096)?max_io_size(filedes):INT_MAX;
553   const char *buf = (const char *)buffer;
554
555   do {
556     written = write(filedes, buf, len>maxio?maxio:len);
557     if (written < 0)
558       break;
559     len -= written;
560     buf += written;
561   } while (len > 0);
562
563   if (written < 0 && len == orig_len)
564     return -1;
565   else
566     return orig_len - len;
567 }
568
569
570 ssize_t
571 mpe_send(int socket, const void *buffer, size_t len, int flags)
572 {
573   int written = 0;
574   int orig_len = len;
575   int maxio = (len>4096)?max_io_size(socket):INT_MAX;
576   const char *buf = (const char *)buffer;
577
578   do {
579     written = send(socket, buf, len>maxio?maxio:len, flags);
580     if (written < 0)
581       break;
582     len -= written;
583     buf += written;
584   } while (len > 0);
585
586   if (written < 0 && len == orig_len)
587     return -1;
588   else
589     return orig_len - len;
590 }
591
592 ssize_t
593 mpe_sendto(int socket, const void *buffer, size_t len,
594        int flags, const struct sockaddr *dest_addr,
595        socklen_t dest_len)
596 {
597   int written = 0;
598   int orig_len = len;
599   int maxio = (len>4096)?max_io_size(socket):INT_MAX;
600   const char *buf = (const char *)buffer;
601
602   do {
603     written = 
604        sendto(socket, buf, len>maxio?maxio:len, flags, dest_addr, dest_len);
605     if (written < 0)
606       break;
607     len -= written;
608     buf += written;
609   } while (len > 0);
610
611   if (written < 0 && len == orig_len)
612     return -1;
613   else
614     return orig_len - len;
615 }
616
617
618 ssize_t
619 mpe_recv(int socket, void *buffer, size_t len, int flags)
620 {
621   int maxio;
622   if (len > 4096 && (len > (maxio = max_io_size(socket))))
623     len = maxio;
624   return recv(socket, buffer, len, flags);
625 }
626
627 ssize_t
628 mpe_recvfrom(int socket, void *buffer, size_t len,
629            int flags, struct sockaddr *address,
630            socklen_t *address_len) 
631 {
632   int maxio;
633   if (len > 4096 && (len > (maxio = max_io_size(socket))))
634     len = maxio;
635   return recvfrom(socket, buffer, len, flags, address, address_len);
636 }
637
638 /*
639    I didn't do thse two:
640 ssize_t mpe_recvmsg(int, struct msghdr *, int);
641 ssize_t mpe_sendmsg(int, const struct msghdr *, int);
642 */
643
644 /* 
645  * On MPE/iX (at least version 6.0), a getsockname()
646  * performed on a socket that is listening
647  * will return INADDR_ANY, even if you used
648  * bind to bind it to a particular IP address.
649  *
650  * (In fact, it appears that the socket always acts as
651  *  if you used INADDR_ANY.)
652  *
653  * Here I save the IP address used in bind
654  * So I can get it in getsockname()
655  *
656  */
657
658 /* I just save 40.  Usually one or two should be enough
659  */
660
661 int
662 mpe_connect(int socket, 
663     const struct sockaddr *address,
664     socklen_t address_len)
665 {
666   int ret = connect(socket, address, address_len);
667   if (ret < 0 && errno == EINPROGRESS)
668   {
669     /* Need to call getsockopt to clear socket error */
670     int socket_error;
671     socklen_t err_size = sizeof(socket_error);
672     (void)getsockopt(socket, SOL_SOCKET, SO_ERROR,
673                           &socket_error, &err_size);
674     errno = EINPROGRESS;
675   }
676   return ret;
677 }
678
679 static struct {
680   int fd;
681   struct in_addr holdaddr;
682 } holdbind[40];
683 #define HOLDBINDLAST ((sizeof(holdbind))/(sizeof(holdbind[0])))
684 static int nextbind;
685
686 /*
687  * Fix peculiarities of bind() on MPE
688  * 1. call GETPRIVMODE to bind to ports < 1024
689  * 2. save IP address for future calls to getsockname
690  * 3. set IP address to 0 (INADDR_ANY)
691  */
692
693 int
694 mpe_bind(int socket, const struct sockaddr *address, socklen_t address_len)
695 {
696    int i;
697    int result;
698    int mpeprivmode=0;
699    extern void GETPRIVMODE(void);
700    extern void GETUSERMODE(void);
701
702    for (i = 0; i<HOLDBINDLAST; i++) {
703      if (holdbind[i].fd == socket)
704        break;
705    }
706    /* If we didn't find previously used slot, use next */
707    if (i == HOLDBINDLAST)
708      i = nextbind;
709
710    holdbind[i].fd = socket;
711
712    memset(&holdbind[i].holdaddr, '\0', sizeof(holdbind[i].holdaddr));
713    if (address->sa_family == AF_INET
714       && address_len >= offsetof(struct sockaddr_in, sin_addr)
715                         +sizeof(struct in_addr)) {
716       holdbind[i].holdaddr = ((struct sockaddr_in *)address)->sin_addr;
717    }
718    if (i == nextbind)
719    {
720      if (++nextbind >= HOLDBINDLAST)
721        nextbind = 0;
722    }
723
724    if (address->sa_family == AF_INET)
725    {
726         /* The address *MUST* stupidly be zero. */
727         ((struct sockaddr_in *)address)->sin_addr.s_addr = INADDR_ANY;
728         /* PRIV mode is required to bind() to ports < 1024. */
729         if (((struct sockaddr_in *)address)->sin_port < 1024 &&
730             ((struct sockaddr_in *)address)->sin_port > 0) {
731             GETPRIVMODE(); /* If this fails, we are aborted by MPE/iX. */
732             mpeprivmode = 1;
733         }
734     }
735     result = bind(socket, address, address_len);
736     if (mpeprivmode)
737     {
738       GETUSERMODE();
739     }
740     return result;
741
742 }
743
744 int 
745 mpe_getsockname(int socket, struct sockaddr *address, socklen_t *address_len)
746 {
747   int ret;
748   ret = getsockname(socket, address, address_len);
749   if (ret == 0 
750       && address->sa_family == AF_INET
751       && *address_len >= offsetof(struct sockaddr_in, sin_addr)
752                         +sizeof(struct in_addr)
753       && ((struct sockaddr_in *)address)->sin_addr.s_addr == INADDR_ANY) {
754     int i;
755     for (i=0; i<HOLDBINDLAST; i++) {
756       if (holdbind[i].fd == socket)
757       {
758         ((struct sockaddr_in *)address)->sin_addr.s_addr 
759             = holdbind[i].holdaddr.s_addr;
760         break;
761       }
762     }
763   }
764   return ret;
765 }
766
767 int 
768 mpe_getpeername(int socket, struct sockaddr *address, socklen_t *address_len)
769 {
770   int ret;
771   ret = getpeername(socket, address, address_len);
772   if (ret == 0)
773   {
774     /* Try a zero-length write to see if socket really connected */
775     int written = write(socket, "", 0);
776     if (written < 0)
777       ret = -1;
778   }
779   return ret;
780 }
781
782
783 static int
784 max_io_size(int filedes)
785 {
786   int save_errno;
787   struct sockaddr sa;
788   int len;
789   int result = INT_MAX; /* all other files */
790
791   save_errno = errno;
792   len = sizeof sa;
793   if (getsockname(filedes, &sa, &len) == -1)
794   {
795      if (errno == EAFNOSUPPORT) /* AF_UNIX socket */
796        result = 4096;
797      errno = save_errno;
798   } else {
799     result = 30000; /* AF_INET sock max */
800   }
801   return result;
802 }