UNIVERSAL.pm and import methods (tests)
[p5sagit/p5-mst-13.2.git] / util.c
diff --git a/util.c b/util.c
index 6097741..fb6c0c0 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,6 +1,6 @@
 /*    util.c
  *
- *    Copyright (c) 1991-1994, Larry Wall
+ *    Copyright (c) 1991-1997, Larry Wall
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
@@ -150,7 +150,7 @@ MEM_SIZE size;
 
 /* safe version of free */
 
-void
+Free_t
 safefree(where)
 Malloc_t where;
 {
@@ -185,13 +185,13 @@ MEM_SIZE size;
     if ((long)size < 0 || (long)count < 0)
        croak("panic: calloc");
 #endif
+    size *= count;
+    ptr = malloc(size?size:1); /* malloc(0) is NASTY on our system */
 #if !(defined(I286) || defined(atarist))
     DEBUG_m(PerlIO_printf(PerlIO_stderr(), "0x%x: (%05d) calloc %ld  x %ld bytes\n",ptr,an++,(long)count,(long)size));
 #else
     DEBUG_m(PerlIO_printf(PerlIO_stderr(), "0x%lx: (%05d) calloc %ld x %ld bytes\n",ptr,an++,(long)count,(long)size));
 #endif
-    size *= count;
-    ptr = malloc(size?size:1); /* malloc(0) is NASTY on our system */
     if (ptr != Nullch) {
        memset((void*)ptr, 0, size);
        return ptr;
@@ -279,31 +279,39 @@ xstat()
 
 #endif /* LEAKTEST */
 
-/* copy a string up to some (non-backslashed) delimiter, if any */
+/* copy a string up to some (non-backslashed) delimiter, if any;
+   If the delimiter is ';', then do not consider backslashes - 
+   used only for PATH on DOSISH systems. */
 
 char *
-cpytill(to,from,fromend,delim,retlen)
+delimcpy(to, toend, from, fromend, delim, retlen)
 register char *to;
+register char *toend;
 register char *from;
 register char *fromend;
 register int delim;
 I32 *retlen;
 {
-    char *origto = to;
-
-    for (; from < fromend; from++,to++) {
-       if (*from == '\\') {
+    register I32 tolen;
+    for (tolen = 0; from < fromend; from++, tolen++) {
+       if (*from == '\\' && delim != ';') {
            if (from[1] == delim)
                from++;
-           else if (from[1] == '\\')
-               *to++ = *from++;
+           else {
+               if (to < toend)
+                   *to++ = *from;
+               tolen++;
+               from++;
+           }
        }
        else if (*from == delim)
            break;
-       *to = *from;
+       if (to < toend)
+           *to++ = *from;
     }
-    *to = '\0';
-    *retlen = to - origto;
+    if (to < toend)
+       *to = '\0';
+    *retlen = tolen;
     return from;
 }
 
@@ -1071,79 +1079,78 @@ register I32 len;
     return newaddr;
 }
 
+/* the SV for form() and mess() is not kept in an arena */
+
+static SV *
+mess_alloc()
+{
+    SV *sv;
+    XPVMG *any;
+
+    /* Create as PVMG now, to avoid any upgrading later */
+    New(905, sv, 1, SV);
+    Newz(905, any, 1, XPVMG);
+    SvFLAGS(sv) = SVt_PVMG;
+    SvANY(sv) = (void*)any;
+    SvREFCNT(sv) = 1 << 30; /* practically infinite */
+    return sv;
+}
+
 #ifdef I_STDARG
 char *
-mess(const char *pat, va_list *args)
+form(const char* pat, ...)
 #else
 /*VARARGS0*/
 char *
-mess(pat, args)
+form(pat, va_alist)
     const char *pat;
-    va_list *args;
+    va_dcl
 #endif
 {
-    char *s;
-    char *s_start;
-    SV *tmpstr;
-    I32 usermess;
-#ifndef HAS_VPRINTF
-#ifdef USE_CHAR_VSPRINTF
-    char *vsprintf();
+    va_list args;
+#ifdef I_STDARG
+    va_start(args, pat);
 #else
-    I32 vsprintf();
-#endif
+    va_start(args);
 #endif
+    if (!mess_sv)
+       mess_sv = mess_alloc();
+    sv_vsetpvfn(mess_sv, pat, strlen(pat), &args, Null(SV**), 0, Null(bool*));
+    va_end(args);
+    return SvPVX(mess_sv);
+}
 
-    s = s_start = buf;
-    usermess = strEQ(pat, "%s");
-    if (usermess) {
-       tmpstr = sv_newmortal();
-       sv_setpv(tmpstr, va_arg(*args, char *));
-       *s++ = SvCUR(tmpstr) ? SvPVX(tmpstr)[SvCUR(tmpstr)-1] : ' ';
-    }
-    else {
-       (void) vsprintf(s,pat,*args);
-       s += strlen(s);
-    }
-    va_end(*args);
+char *
+mess(pat, args)
+    const char *pat;
+    va_list *args;
+{
+    SV *sv;
+    static char dgd[] = " during global destruction.\n";
 
-    if (!(s > s_start && s[-1] == '\n')) {
+    if (!mess_sv)
+       mess_sv = mess_alloc();
+    sv = mess_sv;
+    sv_vsetpvfn(sv, pat, strlen(pat), args, Null(SV**), 0, Null(bool*));
+    if (!SvCUR(sv) || *(SvEND(sv) - 1) != '\n') {
        if (dirty)
-           strcpy(s, " during global destruction.\n");
+           sv_catpv(sv, dgd);
        else {
-           if (curcop->cop_line) {
-               (void)sprintf(s," at %s line %ld",
-                 SvPVX(GvSV(curcop->cop_filegv)), (long)curcop->cop_line);
-               s += strlen(s);
-           }
+           if (curcop->cop_line)
+               sv_catpvf(sv, " at %_ line %ld",
+                         GvSV(curcop->cop_filegv), (long)curcop->cop_line);
            if (GvIO(last_in_gv) && IoLINES(GvIOp(last_in_gv))) {
                bool line_mode = (RsSIMPLE(rs) &&
                                  SvLEN(rs) == 1 && *SvPVX(rs) == '\n');
-               (void)sprintf(s,", <%s> %s %ld",
-                 last_in_gv == argvgv ? "" : GvNAME(last_in_gv),
-                 line_mode ? "line" : "chunk", 
-                 (long)IoLINES(GvIOp(last_in_gv)));
-               s += strlen(s);
+               sv_catpvf(sv, ", <%s> %s %ld",
+                         last_in_gv == argvgv ? "" : GvNAME(last_in_gv),
+                         line_mode ? "line" : "chunk", 
+                         (long)IoLINES(GvIOp(last_in_gv)));
            }
-           (void)strcpy(s,".\n");
-           s += 2;
+           sv_catpv(sv, ".\n");
        }
-       if (usermess)
-           sv_catpv(tmpstr,buf+1);
-    }
-
-    if (s - s_start >= sizeof(buf)) {  /* Ooops! */
-       if (usermess)
-           PerlIO_puts(PerlIO_stderr(), SvPVX(tmpstr));
-       else
-           PerlIO_puts(PerlIO_stderr(), buf);
-       PerlIO_puts(PerlIO_stderr(), "panic: message overflow - memory corrupted!\n");
-       my_exit(1);
     }
-    if (usermess)
-       return SvPVX(tmpstr);
-    else
-       return buf;
+    return SvPVX(sv);
 }
 
 #ifdef I_STDARG
@@ -1159,7 +1166,7 @@ die(pat, va_alist)
 {
     va_list args;
     char *message;
-    int oldrunlevel = runlevel;
+    I32 oldrunlevel = runlevel;
     int was_in_eval = in_eval;
     HV *stash;
     GV *gv;
@@ -1209,7 +1216,7 @@ die(pat, va_alist)
 
     restartop = die_where(message);
     if ((!restartop && was_in_eval) || oldrunlevel > 1)
-       Siglongjmp(top_env, 3);
+       JMPENV_JUMP(3);
     return restartop;
 }
 
@@ -1264,25 +1271,11 @@ croak(pat, va_alist)
     }
     if (in_eval) {
        restartop = die_where(message);
-       Siglongjmp(top_env, 3);
+       JMPENV_JUMP(3);
     }
     PerlIO_puts(PerlIO_stderr(),message);
     (void)PerlIO_flush(PerlIO_stderr());
-    if (e_tmpname) {
-       if (e_fp) {
-           PerlIO_close(e_fp);
-           e_fp = Nullfp;
-       }
-       (void)UNLINK(e_tmpname);
-       Safefree(e_tmpname);
-       e_tmpname = Nullch;
-    }
-    statusvalue = SHIFTSTATUS(statusvalue);
-#ifdef VMS
-    my_exit((U32)(vaxc$errno?vaxc$errno:(statusvalue?statusvalue:44)));
-#else
-    my_exit((U32)((errno&255)?errno:((statusvalue&255)?statusvalue:255)));
-#endif
+    my_failure_exit();
 }
 
 void
@@ -1343,6 +1336,7 @@ warn(pat,va_alist)
 }
 
 #ifndef VMS  /* VMS' my_setenv() is in VMS.c */
+#ifndef WIN32
 void
 my_setenv(nam,val)
 char *nam, *val;
@@ -1363,6 +1357,7 @@ char *nam, *val;
        environ = tmpenv;               /* tell exec where it is now */
     }
     if (!val) {
+       Safefree(environ[i]);
        while (environ[i]) {
            environ[i] = environ[i+1];
            i++;
@@ -1389,6 +1384,74 @@ char *nam, *val;
 #endif /* MSDOS */
 }
 
+#else /* if WIN32 */
+
+void
+my_setenv(nam,val)
+char *nam, *val;
+{
+
+#ifdef USE_WIN32_RTL_ENV
+
+    register char *envstr;
+    STRLEN namlen = strlen(nam);
+    STRLEN vallen;
+    char *oldstr = environ[setenv_getix(nam)];
+
+    /* putenv() has totally broken semantics in both the Borland
+     * and Microsoft CRTLs.  They either store the passed pointer in
+     * the environment without making a copy, or make a copy and don't
+     * free it. And on top of that, they dont free() old entries that
+     * are being replaced/deleted.  This means the caller must
+     * free any old entries somehow, or we end up with a memory
+     * leak every time my_setenv() is called.  One might think
+     * one could directly manipulate environ[], like the UNIX code
+     * above, but direct changes to environ are not allowed when
+     * calling putenv(), since the RTLs maintain an internal
+     * *copy* of environ[]. Bad, bad, *bad* stink.
+     * GSAR 97-06-07
+     */
+
+    if (!val) {
+       if (!oldstr)
+           return;
+       val = "";
+       vallen = 0;
+    }
+    else
+       vallen = strlen(val);
+    New(904, envstr, namlen + vallen + 3, char);
+    (void)sprintf(envstr,"%s=%s",nam,val);
+    (void)putenv(envstr);
+    if (oldstr)
+       Safefree(oldstr);
+#ifdef _MSC_VER
+    Safefree(envstr);          /* MSVCRT leaks without this */
+#endif
+
+#else /* !USE_WIN32_RTL_ENV */
+
+    /* The sane way to deal with the environment.
+     * Has these advantages over putenv() & co.:
+     *  * enables us to store a truly empty value in the
+     *    environment (like in UNIX).
+     *  * we don't have to deal with RTL globals, bugs and leaks.
+     *  * Much faster.
+     * Why you may want to enable USE_WIN32_RTL_ENV:
+     *  * environ[] and RTL functions will not reflect changes,
+     *    which might be an issue if extensions want to access
+     *    the env. via RTL.  This cuts both ways, since RTL will
+     *    not see changes made by extensions that call the Win32
+     *    functions directly, either.
+     * GSAR 97-06-07
+     */
+    SetEnvironmentVariable(nam,val);
+
+#endif
+}
+
+#endif /* WIN32 */
+
 I32
 setenv_getix(nam)
 char *nam;
@@ -1396,11 +1459,18 @@ char *nam;
     register I32 i, len = strlen(nam);
 
     for (i = 0; environ[i]; i++) {
-       if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
+       if (
+#ifdef WIN32
+           strnicmp(environ[i],nam,len) == 0
+#else
+           strnEQ(environ[i],nam,len)
+#endif
+           && environ[i][len] == '=')
            break;                      /* strnEQ must come first to avoid */
     }                                  /* potential SEGV's */
     return i;
 }
+
 #endif /* !VMS */
 
 #ifdef UNLINK_ALL_VERSIONS
@@ -1438,6 +1508,21 @@ register I32 len;
 }
 #endif
 
+#ifndef HAS_MEMSET
+void *
+my_memset(loc,ch,len)
+register char *loc;
+register I32 ch;
+register I32 len;
+{
+    char *retval = loc;
+
+    while (len--)
+       *loc++ = ch;
+    return retval;
+}
+#endif
+
 #if !defined(HAS_BZERO) && !defined(HAS_MEMSET)
 char *
 my_bzero(loc,len)
@@ -1714,7 +1799,7 @@ char      *mode;
        }
        /*SUPPRESS 560*/
        if (tmpgv = gv_fetchpv("$",TRUE, SVt_PV))
-           sv_setiv(GvSV(tmpgv),(I32)getpid());
+           sv_setiv(GvSV(tmpgv), (IV)getpid());
        forkprocess = 0;
        hv_clear(pidstatus);    /* we have no children */
        return Nullfp;
@@ -1778,15 +1863,23 @@ int newfd;
     close(newfd);
     return fcntl(oldfd, F_DUPFD, newfd);
 #else
-    int fdtmp[256];
+#define DUP2_MAX_FDS 256
+    int fdtmp[DUP2_MAX_FDS];
     I32 fdx = 0;
     int fd;
 
     if (oldfd == newfd)
        return oldfd;
     close(newfd);
-    while ((fd = dup(oldfd)) != newfd && fd >= 0) /* good enough for low fd's */
+    /* good enough for low fd's... */
+    while ((fd = dup(oldfd)) != newfd && fd >= 0) {
+       if (fdx >= DUP2_MAX_FDS) {
+           close(fd);
+           fd = -1;
+           break;
+       }
        fdtmp[fdx++] = fd;
+    }
     while (fdx > 0)
        close(fdtmp[--fdx]);
     return fd;
@@ -1917,6 +2010,11 @@ PerlIO *ptr;
     int status;
     SV **svp;
     int pid;
+    bool close_failed;
+    int saved_errno;
+#ifdef VMS
+    int saved_vaxc_errno;
+#endif
 
     svp = av_fetch(fdpid,PerlIO_fileno(ptr),TRUE);
     pid = (int)SvIVX(*svp);
@@ -1927,7 +2025,12 @@ PerlIO *ptr;
        return my_syspclose(ptr);
     }
 #endif 
-    PerlIO_close(ptr);
+    if ((close_failed = (PerlIO_close(ptr) == EOF))) {
+       saved_errno = errno;
+#ifdef VMS
+       saved_vaxc_errno = vaxc$errno;
+#endif
+    }
 #ifdef UTS
     if(kill(pid, 0) < 0) { return(pid); }   /* HOM 12/23/91 */
 #endif
@@ -1940,7 +2043,11 @@ PerlIO *ptr;
     rsignal_restore(SIGHUP, &hstat);
     rsignal_restore(SIGINT, &istat);
     rsignal_restore(SIGQUIT, &qstat);
-    return(pid < 0 ? pid : status);
+    if (close_failed) {
+       SETERRNO(saved_errno, saved_vaxc_errno);
+       return -1;
+    }
+    return(pid < 0 ? pid : status == 0 ? 0 : (errno = 0, status));
 }
 #endif /* !DOSISH */
 
@@ -1953,7 +2060,7 @@ int flags;
 {
     SV *sv;
     SV** svp;
-    char spid[16];
+    char spid[TYPE_CHARS(int)];
 
     if (!pid)
        return -1;
@@ -2009,7 +2116,7 @@ int pid;
 int status;
 {
     register SV *sv;
-    char spid[16];
+    char spid[TYPE_CHARS(int)];
 
     sprintf(spid, "%d", pid);
     sv = *hv_fetch(pidstatus,spid,strlen(spid),TRUE);
@@ -2147,10 +2254,7 @@ char *b;
     char *fb = strrchr(b,'/');
     struct stat tmpstatbuf1;
     struct stat tmpstatbuf2;
-#ifndef MAXPATHLEN
-#define MAXPATHLEN 1024
-#endif
-    char tmpbuf[MAXPATHLEN+1];
+    SV *tmpsv = sv_newmortal();
 
     if (fa)
        fa++;
@@ -2163,16 +2267,16 @@ char *b;
     if (strNE(a,b))
        return FALSE;
     if (fa == a)
-       strcpy(tmpbuf,".");
+       sv_setpv(tmpsv, ".");
     else
-       strncpy(tmpbuf, a, fa - a);
-    if (Stat(tmpbuf, &tmpstatbuf1) < 0)
+       sv_setpvn(tmpsv, a, fa - a);
+    if (Stat(SvPVX(tmpsv), &tmpstatbuf1) < 0)
        return FALSE;
     if (fb == b)
-       strcpy(tmpbuf,".");
+       sv_setpv(tmpsv, ".");
     else
-       strncpy(tmpbuf, b, fb - b);
-    if (Stat(tmpbuf, &tmpstatbuf2) < 0)
+       sv_setpvn(tmpsv, b, fb - b);
+    if (Stat(SvPVX(tmpsv), &tmpstatbuf2) < 0)
        return FALSE;
     return tmpstatbuf1.st_dev == tmpstatbuf2.st_dev &&
           tmpstatbuf1.st_ino == tmpstatbuf2.st_ino;