Make chr() for values >127 to create utf8 when under utf8.
[p5sagit/p5-mst-13.2.git] / pp_sys.c
index ffe6af9..1ea47cf 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
 #ifdef I_SHADOW
 /* Shadow password support for solaris - pdo@cs.umd.edu
  * Not just Solaris: at least HP-UX, IRIX, Linux.
- * the API is from SysV. --jhi */
-#ifdef __hpux__
+ * The API is from SysV.
+ *
+ * There are at least two more shadow interfaces,
+ * see the comments in pp_gpwent().
+ *
+ * --jhi */
+#   ifdef __hpux__
 /* There is a MAXINT coming from <shadow.h> <- <hpsecurity.h> <- <values.h>
  * and another MAXINT from "perl.h" <- <sys/param.h>. */ 
-#undef MAXINT
-#endif
-#include <shadow.h>
+#       undef MAXINT
+#   endif
+#   include <shadow.h>
 #endif
 
 /* XXX If this causes problems, set i_unistd=undef in the hint file.  */
@@ -195,10 +200,9 @@ static char zero_but_true[ZBTLEN + 1] = "0 but true";
 #endif
 
 #if !defined(PERL_EFF_ACCESS_R_OK) && defined(HAS_EACCESS)
-#   if defined(I_SYS_SECURITY)
+#   ifdef I_SYS_SECURITY
 #       include <sys/security.h>
 #   endif
-    /* XXX Configure test needed for eaccess */
 #   ifdef ACC_SELF
         /* HP SecureWare */
 #       define PERL_EFF_ACCESS_R_OK(p) (eaccess((p), R_OK, ACC_SELF))
@@ -304,9 +308,14 @@ PP(pp_backtick)
     STRLEN n_a;
     char *tmps = POPpx;
     I32 gimme = GIMME_V;
+    char *mode = "r";
 
     TAINT_PROPER("``");
-    fp = PerlProc_popen(tmps, "r");
+    if (PL_op->op_private & OPpOPEN_IN_RAW)
+       mode = "rb";
+    else if (PL_op->op_private & OPpOPEN_IN_CRLF)
+       mode = "rt";
+    fp = PerlProc_popen(tmps, mode);
     if (fp) {
        if (gimme == G_VOID) {
            char tmpbuf[256];
@@ -356,6 +365,10 @@ PP(pp_glob)
     OP *result;
     tryAMAGICunTARGET(iter, -1);
 
+    /* Note that we only ever get here if File::Glob fails to load
+     * without at the same time croaking, for some reason, or if
+     * perl was built with PERL_EXTERNAL_GLOB */
+
     ENTER;
 
 #ifndef VMS
@@ -461,7 +474,7 @@ PP(pp_die)
                GV *gv = gv_fetchmethod(stash, "PROPAGATE");
                if (gv) {
                    SV *file = sv_2mortal(newSVpv(CopFILE(PL_curcop),0));
-                   SV *line = sv_2mortal(newSViv(CopLINE(PL_curcop)));
+                   SV *line = sv_2mortal(newSVuv(CopLINE(PL_curcop)));
                    EXTEND(SP, 3);
                    PUSHMARK(SP);
                    PUSHs(error);
@@ -517,7 +530,7 @@ PP(pp_open)
     if (GvIOp(gv))
        IoFLAGS(GvIOp(gv)) &= ~IOf_UNTAINT;
 
-    if (mg = SvTIED_mg((SV*)gv, 'q')) {
+    if ((mg = SvTIED_mg((SV*)gv, 'q'))) {
        PUSHMARK(SP);
        XPUSHs(SvTIED_obj((SV*)gv, mg));
        XPUSHs(sv);
@@ -552,7 +565,7 @@ PP(pp_close)
     else
        gv = (GV*)POPs;
 
-    if (mg = SvTIED_mg((SV*)gv, 'q')) {
+    if ((mg = SvTIED_mg((SV*)gv, 'q'))) {
        PUSHMARK(SP);
        XPUSHs(SvTIED_obj((SV*)gv, mg));
        PUTBACK;
@@ -683,15 +696,20 @@ PP(pp_binmode)
     IO *io;
     PerlIO *fp;
     MAGIC *mg;
+    SV *discp = Nullsv;
 
     if (MAXARG < 1)
        RETPUSHUNDEF;
+    if (MAXARG > 1)
+       discp = POPs;
 
     gv = (GV*)POPs; 
 
     if (gv && (mg = SvTIED_mg((SV*)gv, 'q'))) {
        PUSHMARK(SP);
        XPUSHs(SvTIED_obj((SV*)gv, mg));
+       if (discp)
+           XPUSHs(discp);
        PUTBACK;
        ENTER;
        call_method("BINMODE", G_SCALAR);
@@ -704,13 +722,12 @@ PP(pp_binmode)
     if (!(io = GvIO(gv)) || !(fp = IoIFP(io)))
        RETPUSHUNDEF;
 
-    if (do_binmode(fp,IoTYPE(io),TRUE)) 
+    if (do_binmode(fp,IoTYPE(io),mode_from_discipline(discp))) 
        RETPUSHYES;
     else
        RETPUSHUNDEF;
 }
 
-
 PP(pp_tie)
 {
     djSP;
@@ -793,7 +810,7 @@ PP(pp_untie)
 
     if (ckWARN(WARN_UNTIE)) {
         MAGIC * mg ;
-        if (mg = SvTIED_mg(sv, how)) {
+        if ((mg = SvTIED_mg(sv, how))) {
             if (mg && SvREFCNT(SvRV(mg->mg_obj)) > 1)  
                Perl_warner(aTHX_ WARN_UNTIE,
                    "untie attempted while %"UVuf" inner references still exist",
@@ -812,7 +829,7 @@ PP(pp_tied)
     char how = (SvTYPE(sv) == SVt_PVHV || SvTYPE(sv) == SVt_PVAV) ? 'P' : 'q';
     MAGIC *mg;
 
-    if (mg = SvTIED_mg(sv, how)) {
+    if ((mg = SvTIED_mg(sv, how))) {
        SV *osv = SvTIED_obj(sv, mg);
        if (osv == mg->mg_obj)
            osv = sv_mortalcopy(osv);
@@ -851,9 +868,9 @@ PP(pp_dbmopen)
     PUSHs(sv);
     PUSHs(left);
     if (SvIV(right))
-       PUSHs(sv_2mortal(newSViv(O_RDWR|O_CREAT)));
+       PUSHs(sv_2mortal(newSVuv(O_RDWR|O_CREAT)));
     else
-       PUSHs(sv_2mortal(newSViv(O_RDWR)));
+       PUSHs(sv_2mortal(newSVuv(O_RDWR)));
     PUSHs(right);
     PUTBACK;
     call_sv((SV*)GvCV(gv), G_SCALAR);
@@ -864,7 +881,7 @@ PP(pp_dbmopen)
        PUSHMARK(SP);
        PUSHs(sv);
        PUSHs(left);
-       PUSHs(sv_2mortal(newSViv(O_RDONLY)));
+       PUSHs(sv_2mortal(newSVuv(O_RDONLY)));
        PUSHs(right);
        PUTBACK;
        call_sv((SV*)GvCV(gv), G_SCALAR);
@@ -1076,12 +1093,12 @@ PP(pp_getc)
     GV *gv;
     MAGIC *mg;
 
-    if (MAXARG <= 0)
+    if (MAXARG == 0)
        gv = PL_stdingv;
     else
        gv = (GV*)POPs;
 
-    if (mg = SvTIED_mg((SV*)gv, 'q')) {
+    if ((mg = SvTIED_mg((SV*)gv, 'q'))) {
        I32 gimme = GIMME_V;
        PUSHMARK(SP);
        XPUSHs(SvTIED_obj((SV*)gv, mg));
@@ -1305,7 +1322,7 @@ PP(pp_prtf)
     else
        gv = PL_defoutgv;
 
-    if (mg = SvTIED_mg((SV*)gv, 'q')) {
+    if ((mg = SvTIED_mg((SV*)gv, 'q'))) {
        if (MARK == ORIGMARK) {
            MEXTEND(SP, 1);
            ++MARK;
@@ -1575,10 +1592,11 @@ PP(pp_send)
     djSP; dMARK; dORIGMARK; dTARGET;
     GV *gv;
     IO *io;
-    Off_t offset;
     SV *bufsv;
     char *buffer;
-    Off_t length;
+    Size_t length;
+    SSize_t retval;
+    IV offset;
     STRLEN blen;
     MAGIC *mg;
 
@@ -1601,17 +1619,17 @@ PP(pp_send)
        goto say_undef;
     bufsv = *++MARK;
     buffer = SvPV(bufsv, blen);
-#if Off_t_SIZE > IVSIZE
-    length = SvNVx(*++MARK);
+#if Size_t_size > IVSIZE
+    length = (Size_t)SvNVx(*++MARK);
 #else
-    length = SvIVx(*++MARK);
+    length = (Size_t)SvIVx(*++MARK);
 #endif
-    if (length < 0)
+    if ((SSize_t)length < 0)
        DIE(aTHX_ "Negative length");
     SETERRNO(0,0);
     io = GvIO(gv);
     if (!io || !IoIFP(io)) {
-       length = -1;
+       retval = -1;
        if (ckWARN(WARN_CLOSED)) {
            if (PL_op->op_type == OP_SYSWRITE)
                report_closed_fh(gv, io, "syswrite", "filehandle");
@@ -1621,11 +1639,7 @@ PP(pp_send)
     }
     else if (PL_op->op_type == OP_SYSWRITE) {
        if (MARK < SP) {
-#if Off_t_SIZE > IVSIZE
-           offset = SvNVx(*++MARK);
-#else
            offset = SvIVx(*++MARK);
-#endif
            if (offset < 0) {
                if (-offset > blen)
                    DIE(aTHX_ "Offset outside string");
@@ -1638,14 +1652,14 @@ PP(pp_send)
            length = blen - offset;
 #ifdef PERL_SOCK_SYSWRITE_IS_SEND
        if (IoTYPE(io) == 's') {
-           length = PerlSock_send(PerlIO_fileno(IoIFP(io)),
+           retval = PerlSock_send(PerlIO_fileno(IoIFP(io)),
                                   buffer+offset, length, 0);
        }
        else
 #endif
        {
            /* See the note at doio.c:do_print about filesize limits. --jhi */
-           length = PerlLIO_write(PerlIO_fileno(IoIFP(io)),
+           retval = PerlLIO_write(PerlIO_fileno(IoIFP(io)),
                                   buffer+offset, length);
        }
     }
@@ -1654,20 +1668,24 @@ PP(pp_send)
        char *sockbuf;
        STRLEN mlen;
        sockbuf = SvPVx(*++MARK, mlen);
-       length = PerlSock_sendto(PerlIO_fileno(IoIFP(io)), buffer, blen, length,
-                               (struct sockaddr *)sockbuf, mlen);
+       retval = PerlSock_sendto(PerlIO_fileno(IoIFP(io)), buffer, blen,
+                                length, (struct sockaddr *)sockbuf, mlen);
     }
     else
-       length = PerlSock_send(PerlIO_fileno(IoIFP(io)), buffer, blen, length);
+       retval = PerlSock_send(PerlIO_fileno(IoIFP(io)), buffer, blen, length);
 
 #else
     else
        DIE(aTHX_ PL_no_sock_func, "send");
 #endif
-    if (length < 0)
+    if (retval < 0)
        goto say_undef;
     SP = ORIGMARK;
-    PUSHi(length);
+#if Size_t_size > IVSIZE
+    PUSHn(retval);
+#else
+    PUSHi(retval);
+#endif
     RETURN;
 
   say_undef:
@@ -1686,7 +1704,7 @@ PP(pp_eof)
     GV *gv;
     MAGIC *mg;
 
-    if (MAXARG <= 0) {
+    if (MAXARG == 0) {
        if (PL_op->op_flags & OPf_SPECIAL) {    /* eof() */
            IO *io;
            gv = PL_last_in_gv = PL_argvgv;
@@ -1730,7 +1748,7 @@ PP(pp_tell)
     GV *gv;     
     MAGIC *mg;
 
-    if (MAXARG <= 0)
+    if (MAXARG == 0)
        gv = PL_last_in_gv;
     else
        gv = PL_last_in_gv = (GV*)POPs;
@@ -1779,9 +1797,9 @@ PP(pp_sysseek)
 #if LSEEKSIZE > IVSIZE
        XPUSHs(sv_2mortal(newSVnv((NV) offset)));
 #else
-       XPUSHs(sv_2mortal(newSViv((IV) offset)));
+       XPUSHs(sv_2mortal(newSViv(offset)));
 #endif
-       XPUSHs(sv_2mortal(newSViv((IV) whence)));
+       XPUSHs(sv_2mortal(newSViv(whence)));
        PUTBACK;
        ENTER;
        call_method("SEEK", G_SCALAR);
@@ -1793,15 +1811,15 @@ PP(pp_sysseek)
     if (PL_op->op_type == OP_SEEK)
        PUSHs(boolSV(do_seek(gv, offset, whence)));
     else {
-       Off_t n = do_sysseek(gv, offset, whence);
-        if (n < 0)
+       Off_t sought = do_sysseek(gv, offset, whence);
+        if (sought < 0)
             PUSHs(&PL_sv_undef);
         else {
-            SV* sv = n ?
+            SV* sv = sought ?
 #if LSEEKSIZE > IVSIZE
-                newSVnv((NV)n)
+                newSVnv((NV)sought)
 #else
-                newSViv((IV)n)
+                newSViv(sought)
 #endif
                 : newSVpvn(zero_but_true, ZBTLEN);
             PUSHs(sv_2mortal(sv));
@@ -1813,11 +1831,24 @@ PP(pp_sysseek)
 PP(pp_truncate)
 {
     djSP;
-    Off_t len = (Off_t)POPn;
+    /* There seems to be no consensus on the length type of truncate()
+     * and ftruncate(), both off_t and size_t have supporters. In
+     * general one would think that when using large files, off_t is
+     * at least as wide as size_t, so using an off_t should be okay. */
+    /* XXX Configure probe for the length type of *truncate() needed XXX */
+    Off_t len;
     int result = 1;
     GV *tmpgv;
     STRLEN n_a;
 
+#if Size_t_size > IVSIZE
+    len = (Off_t)POPn;
+#else
+    len = (Off_t)POPi;
+#endif
+    /* Checking for length < 0 is problematic as the type might or
+     * might not be signed: if it is not, clever compilers will moan. */ 
+    /* XXX Configure probe for the signedness of the length type of *truncate() needed? XXX */
     SETERRNO(0,0);
 #if defined(HAS_TRUNCATE) || defined(HAS_CHSIZE) || defined(F_FREESP)
     if (PL_op->op_flags & OPf_SPECIAL) {
@@ -1965,7 +1996,7 @@ PP(pp_flock)
 
 #ifdef FLOCK
     argtype = POPi;
-    if (MAXARG <= 0)
+    if (MAXARG == 0)
        gv = PL_last_in_gv;
     else
        gv = (GV*)POPs;
@@ -2514,17 +2545,25 @@ PP(pp_stat)
        EXTEND_MORTAL(max);
        PUSHs(sv_2mortal(newSViv(PL_statcache.st_dev)));
        PUSHs(sv_2mortal(newSViv(PL_statcache.st_ino)));
-       PUSHs(sv_2mortal(newSViv(PL_statcache.st_mode)));
-       PUSHs(sv_2mortal(newSViv(PL_statcache.st_nlink)));
+       PUSHs(sv_2mortal(newSVuv(PL_statcache.st_mode)));
+       PUSHs(sv_2mortal(newSVuv(PL_statcache.st_nlink)));
 #if Uid_t_size > IVSIZE
        PUSHs(sv_2mortal(newSVnv(PL_statcache.st_uid)));
 #else
+#   if Uid_t_sign <= 0
        PUSHs(sv_2mortal(newSViv(PL_statcache.st_uid)));
+#   else
+       PUSHs(sv_2mortal(newSVuv(PL_statcache.st_uid)));
+#   endif
 #endif
 #if Gid_t_size > IVSIZE 
        PUSHs(sv_2mortal(newSVnv(PL_statcache.st_gid)));
 #else
+#   if Gid_t_sign <= 0
        PUSHs(sv_2mortal(newSViv(PL_statcache.st_gid)));
+#   else
+       PUSHs(sv_2mortal(newSVuv(PL_statcache.st_gid)));
+#   endif
 #endif
 #ifdef USE_STAT_RDEV
        PUSHs(sv_2mortal(newSViv(PL_statcache.st_rdev)));
@@ -2546,8 +2585,8 @@ PP(pp_stat)
        PUSHs(sv_2mortal(newSViv(PL_statcache.st_ctime)));
 #endif
 #ifdef USE_STAT_BLOCKS
-       PUSHs(sv_2mortal(newSViv(PL_statcache.st_blksize)));
-       PUSHs(sv_2mortal(newSViv(PL_statcache.st_blocks)));
+       PUSHs(sv_2mortal(newSVuv(PL_statcache.st_blksize)));
+       PUSHs(sv_2mortal(newSVuv(PL_statcache.st_blocks)));
 #else
        PUSHs(sv_2mortal(newSVpvn("", 0)));
        PUSHs(sv_2mortal(newSVpvn("", 0)));
@@ -3043,7 +3082,7 @@ PP(pp_fttext)
            (void)PerlIO_close(fp);
            RETPUSHUNDEF;
        }
-       do_binmode(fp, '<', TRUE);
+       do_binmode(fp, '<', O_BINARY);
        len = PerlIO_read(fp, tbuf, sizeof(tbuf));
        (void)PerlIO_close(fp);
        if (len <= 0) {
@@ -3074,9 +3113,26 @@ PP(pp_fttext)
 #else
        else if (*s & 128) {
 #ifdef USE_LOCALE
-           if (!(PL_op->op_private & OPpLOCALE) || !isALPHA_LC(*s))
-#endif
-               odd++;
+           if ((PL_op->op_private & OPpLOCALE) && isALPHA_LC(*s))
+               continue;
+#endif
+           /* utf8 characters don't count as odd */
+           if (*s & 0x40) {
+               int ulen = UTF8SKIP(s);
+               if (ulen < len - i) {
+                   int j;
+                   for (j = 1; j < ulen; j++) {
+                       if ((s[j] & 0xc0) != 0x80)
+                           goto not_utf8;
+                   }
+                   --ulen;     /* loop does extra increment */
+                   s += ulen;
+                   i += ulen;
+                   continue;
+               }
+           }
+         not_utf8:
+           odd++;
        }
        else if (*s < 32 &&
          *s != '\n' && *s != '\r' && *s != '\b' &&
@@ -3455,14 +3511,15 @@ PP(pp_readdir)
 
     if (GIMME == G_ARRAY) {
        /*SUPPRESS 560*/
-       while (dp = (Direntry_t *)PerlDir_read(IoDIRP(io))) {
+       while ((dp = (Direntry_t *)PerlDir_read(IoDIRP(io)))) {
 #ifdef DIRNAMLEN
            sv = newSVpvn(dp->d_name, dp->d_namlen);
 #else
            sv = newSVpv(dp->d_name, 0);
 #endif
 #ifndef INCOMPLETE_TAINTS
-           SvTAINTED_on(sv);
+           if (!(IoFLAGS(io) & IOf_UNTAINT))
+               SvTAINTED_on(sv);
 #endif
            XPUSHs(sv_2mortal(sv));
        }
@@ -3476,7 +3533,8 @@ PP(pp_readdir)
        sv = newSVpv(dp->d_name, 0);
 #endif
 #ifndef INCOMPLETE_TAINTS
-       SvTAINTED_on(sv);
+       if (!(IoFLAGS(io) & IOf_UNTAINT))
+           SvTAINTED_on(sv);
 #endif
        XPUSHs(sv_2mortal(sv));
     }
@@ -3612,7 +3670,7 @@ PP(pp_fork)
        RETSETUNDEF;
     if (!childpid) {
        /*SUPPRESS 560*/
-       if (tmpgv = gv_fetchpv("$", TRUE, SVt_PV))
+       if ((tmpgv = gv_fetchpv("$", TRUE, SVt_PV)))
            sv_setiv(GvSV(tmpgv), (IV)PerlProc_getpid());
        hv_clear(PL_pidstatus); /* no kids, so don't wait for 'em */
     }
@@ -3626,6 +3684,8 @@ PP(pp_fork)
     EXTEND(SP, 1);
     PERL_FLUSHALL_FOR_CHILD;
     childpid = PerlProc_fork();
+    if (childpid == -1)
+       RETSETUNDEF;
     PUSHi(childpid);
     RETURN;
 #  else
@@ -3689,7 +3749,7 @@ PP(pp_system)
        }
     }
     PERL_FLUSHALL_FOR_CHILD;
-#if (defined(HAS_FORK) || defined(AMIGAOS)) && !defined(VMS) && !defined(OS2)
+#if (defined(HAS_FORK) || defined(AMIGAOS)) && !defined(VMS) && !defined(OS2) && !defined(__CYGWIN__) || defined(PERL_MICRO)
     if (PerlProc_pipe(pp) >= 0)
        did_pipes = 1;
     while ((childpid = vfork()) == -1) {
@@ -3708,13 +3768,17 @@ PP(pp_system)
     if (childpid > 0) {
        if (did_pipes)
            PerlLIO_close(pp[1]);
+#ifndef PERL_MICRO
        rsignal_save(SIGINT, SIG_IGN, &ihand);
        rsignal_save(SIGQUIT, SIG_IGN, &qhand);
+#endif
        do {
            result = wait4pid(childpid, &status, 0);
        } while (result == -1 && errno == EINTR);
+#ifndef PERL_MICRO
        (void)rsignal_restore(SIGINT, &ihand);
        (void)rsignal_restore(SIGQUIT, &qhand);
+#endif
        STATUS_NATIVE_SET(result == -1 ? -1 : status);
        do_execfree();  /* free any memory child malloced on vfork */
        SP = ORIGMARK;
@@ -4029,7 +4093,6 @@ PP(pp_gmtime)
     EXTEND(SP, 9);
     EXTEND_MORTAL(9);
     if (GIMME != G_ARRAY) {
-       dTARGET;
        SV *tsv;
        if (!tmbuf)
            RETPUSHUNDEF;
@@ -4717,46 +4780,88 @@ PP(pp_gpwent)
 #ifdef HAS_PASSWD
     I32 which = PL_op->op_type;
     register SV *sv;
-    struct passwd *pwent;
     STRLEN n_a;
-#if defined(HAS_GETSPENT) || defined(HAS_GETSPNAM)
-    struct spwd *spwent = NULL;
-#endif
+    struct passwd *pwent  = NULL;
+    /* 
+     * We currently support only the SysV getsp* shadow password interface.
+     * The interface is declared in <shadow.h> and often one needs to link
+     * with -lsecurity or some such.
+     * This interface is used at least by Solaris, HP-UX, IRIX, and Linux.
+     * (and SCO?)
+     *
+     * AIX getpwnam() is clever enough to return the encrypted password
+     * only if the caller (euid?) is root.
+     *
+     * There are at least two other shadow password APIs.  Many platforms
+     * seem to contain more than one interface for accessing the shadow
+     * password databases, possibly for compatibility reasons.
+     * The getsp*() is by far he simplest one, the other two interfaces
+     * are much more complicated, but also very similar to each other.
+     *
+     * <sys/types.h>
+     * <sys/security.h>
+     * <prot.h>
+     * struct pr_passwd *getprpw*();
+     * The password is in
+     * char getprpw*(...).ufld.fd_encrypt[]
+     * Mention HAS_GETPRPWNAM here so that Configure probes for it.
+     *
+     * <sys/types.h>
+     * <sys/security.h>
+     * <prot.h>
+     * struct es_passwd *getespw*();
+     * The password is in
+     * char *(getespw*(...).ufld.fd_encrypt)
+     * Mention HAS_GETESPWNAM here so that Configure probes for it.
+     *
+     * Mention I_PROT here so that Configure probes for it.
+     *
+     * In HP-UX for getprpw*() the manual page claims that one should include
+     * <hpsecurity.h> instead of <sys/security.h>, but that is not needed
+     * if one includes <shadow.h> as that includes <hpsecurity.h>,
+     * and pp_sys.c already includes <shadow.h> if there is such.
+     *
+     * Note that <sys/security.h> is already probed for, but currently
+     * it is only included in special cases.
+     * 
+     * In Digital UNIX/Tru64 if using the getespw*() (which seems to be
+     * be preferred interface, even though also the getprpw*() interface
+     * is available) one needs to link with -lsecurity -ldb -laud -lm.
+     * One also needs to call set_auth_parameters() in main() before
+     * doing anything else, whether one is using getespw*() or getprpw*().
+     *
+     * Note that accessing the shadow databases can be magnitudes
+     * slower than accessing the standard databases.
+     *
+     * --jhi
+     */
 
-    if (which == OP_GPWNAM)
-       pwent = getpwnam(POPpx);
-    else if (which == OP_GPWUID)
-       pwent = getpwuid(POPi);
-    else
-#ifdef HAS_GETPWENT
-       pwent = (struct passwd *)getpwent();
-#else
+    switch (which) {
+    case OP_GPWNAM:
+       pwent  = getpwnam(POPpx);
+       break;
+    case OP_GPWUID:
+       pwent = getpwuid((Uid_t)POPi);
+       break;
+    case OP_GPWENT:
+#   ifdef HAS_GETPWENT
+       pwent  = getpwent();
+#   else
        DIE(aTHX_ PL_no_func, "getpwent");
-#endif
-
-#ifdef HAS_GETSPNAM
-    if (which == OP_GPWNAM) {
-       if (pwent)
-           spwent = getspnam(pwent->pw_name);
-    }
-#  ifdef HAS_GETSPUID /* AFAIK there isn't any anywhere. --jhi */ 
-    else if (which == OP_GPWUID) {
-       if (pwent)
-           spwent = getspnam(pwent->pw_name);
+#   endif
+       break;
     }
-#  endif
-#  ifdef HAS_GETSPENT
-    else
-       spwent = (struct spwd *)getspent();
-#  endif
-#endif
 
     EXTEND(SP, 10);
     if (GIMME != G_ARRAY) {
        PUSHs(sv = sv_newmortal());
        if (pwent) {
            if (which == OP_GPWNAM)
+#   if Uid_t_sign <= 0
                sv_setiv(sv, (IV)pwent->pw_uid);
+#   else
+               sv_setuv(sv, (UV)pwent->pw_uid);
+#   endif
            else
                sv_setpv(sv, pwent->pw_name);
        }
@@ -4768,66 +4873,112 @@ PP(pp_gpwent)
        sv_setpv(sv, pwent->pw_name);
 
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
-#ifdef PWPASSWD
-#   if defined(HAS_GETSPENT) || defined(HAS_GETSPNAM)
-      if (spwent)
-              sv_setpv(sv, spwent->sp_pwdp);
-      else
-              sv_setpv(sv, pwent->pw_passwd);
-#   else
-       sv_setpv(sv, pwent->pw_passwd);
+       SvPOK_off(sv);
+       /* If we have getspnam(), we try to dig up the shadow
+        * password.  If we are underprivileged, the shadow
+        * interface will set the errno to EACCES or similar,
+        * and return a null pointer.  If this happens, we will
+        * use the dummy password (usually "*" or "x") from the
+        * standard password database.
+        *
+        * In theory we could skip the shadow call completely
+        * if euid != 0 but in practice we cannot know which
+        * security measures are guarding the shadow databases
+        * on a random platform.
+        *
+        * Resist the urge to use additional shadow interfaces.
+        * Divert the urge to writing an extension instead.
+        *
+        * --jhi */
+#   ifdef HAS_GETSPNAM
+       {
+           struct spwd *spwent;
+           int saverrno; /* Save and restore errno so that
+                          * underprivileged attempts seem
+                          * to have never made the unsccessful
+                          * attempt to retrieve the shadow password. */
+
+           saverrno = errno;
+           spwent = getspnam(pwent->pw_name);
+           errno = saverrno;
+           if (spwent && spwent->sp_pwdp)
+               sv_setpv(sv, spwent->sp_pwdp);
+       }
+#   endif
+       if (!SvPOK(sv)) /* Use the standard password, then. */
+           sv_setpv(sv, pwent->pw_passwd);
+
+#   ifndef INCOMPLETE_TAINTS
+       /* passwd is tainted because user himself can diddle with it.
+        * admittedly not much and in a very limited way, but nevertheless. */
+       SvTAINTED_on(sv);
 #   endif
-#endif
 
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
+#   if Uid_t_sign <= 0
        sv_setiv(sv, (IV)pwent->pw_uid);
+#   else
+       sv_setuv(sv, (UV)pwent->pw_uid);
+#   endif
 
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
+#   if Uid_t_sign <= 0
        sv_setiv(sv, (IV)pwent->pw_gid);
-
-       /* pw_change, pw_quota, and pw_age are mutually exclusive. */
+#   else
+       sv_setuv(sv, (UV)pwent->pw_gid);
+#   endif
+       /* pw_change, pw_quota, and pw_age are mutually exclusive--
+        * because of the poor interface of the Perl getpw*(),
+        * not because there's some standard/convention saying so.
+        * A better interface would have been to return a hash,
+        * but we are accursed by our history, alas. --jhi.  */
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
-#ifdef PWCHANGE
+#   ifdef PWCHANGE
        sv_setiv(sv, (IV)pwent->pw_change);
-#else
-#   ifdef PWQUOTA
-       sv_setiv(sv, (IV)pwent->pw_quota);
 #   else
-#       ifdef PWAGE
+#       ifdef PWQUOTA
+       sv_setiv(sv, (IV)pwent->pw_quota);
+#       else
+#           ifdef PWAGE
        sv_setpv(sv, pwent->pw_age);
+#           endif
 #       endif
 #   endif
-#endif
 
-       /* pw_class and pw_comment are mutually exclusive. */
+       /* pw_class and pw_comment are mutually exclusive--.
+        * see the above note for pw_change, pw_quota, and pw_age. */
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
-#ifdef PWCLASS
+#   ifdef PWCLASS
        sv_setpv(sv, pwent->pw_class);
-#else
-#   ifdef PWCOMMENT
+#   else
+#       ifdef PWCOMMENT
        sv_setpv(sv, pwent->pw_comment);
+#       endif
 #   endif
-#endif
 
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
-#ifdef PWGECOS
+#   ifdef PWGECOS
        sv_setpv(sv, pwent->pw_gecos);
-#endif
-#ifndef INCOMPLETE_TAINTS
+#   endif
+#   ifndef INCOMPLETE_TAINTS
        /* pw_gecos is tainted because user himself can diddle with it. */
        SvTAINTED_on(sv);
-#endif
+#   endif
 
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
        sv_setpv(sv, pwent->pw_dir);
 
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
        sv_setpv(sv, pwent->pw_shell);
+#   ifndef INCOMPLETE_TAINTS
+       /* pw_shell is tainted because user himself can diddle with it. */
+       SvTAINTED_on(sv);
+#   endif
 
-#ifdef PWEXPIRE
+#   ifdef PWEXPIRE
        PUSHs(sv = sv_mortalcopy(&PL_sv_no));
        sv_setiv(sv, (IV)pwent->pw_expire);
-#endif
+#   endif
     }
     RETURN;
 #else
@@ -4840,9 +4991,6 @@ PP(pp_spwent)
     djSP;
 #if defined(HAS_PASSWD) && defined(HAS_SETPWENT)
     setpwent();
-#   ifdef HAS_SETSPENT
-    setspent();
-#   endif
     RETPUSHYES;
 #else
     DIE(aTHX_ PL_no_func, "setpwent");
@@ -4854,9 +5002,6 @@ PP(pp_epwent)
     djSP;
 #if defined(HAS_PASSWD) && defined(HAS_ENDPWENT)
     endpwent();
-#   ifdef HAS_ENDSPENT
-    endspent();
-#   endif
     RETPUSHYES;
 #else
     DIE(aTHX_ PL_no_func, "endpwent");
@@ -4987,7 +5132,6 @@ PP(pp_syscall)
     unsigned long a[20];
     register I32 i = 0;
     I32 retval = -1;
-    MAGIC *mg;
     STRLEN n_a;
 
     if (PL_tainting) {