Add to MANIFEST: README.threads, lib/ISA.pm, lib/Class/Fields.pm
[p5sagit/p5-mst-13.2.git] / util.c
diff --git a/util.c b/util.c
index 0316269..8fa30a0 100644 (file)
--- a/util.c
+++ b/util.c
@@ -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;
@@ -282,28 +282,34 @@ xstat()
 /* copy a string up to some (non-backslashed) delimiter, if any */
 
 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++) {
+    register I32 tolen;
+    for (tolen = 0; from < fromend; from++, tolen++) {
        if (*from == '\\') {
            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 +1077,79 @@ 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);
-
-    if (!(s > s_start && s[-1] == '\n')) {
+char *
+mess(pat, args)
+    const char *pat;
+    va_list *args;
+{
+    SV *sv;
+    static char dgd[] = " during global destruction.\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') {
+       dTHR;
        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
@@ -1157,14 +1163,17 @@ die(pat, va_alist)
     va_dcl
 #endif
 {
+    dTHR;
     va_list args;
     char *message;
-    int oldrunlevel = runlevel;
+    I32 oldrunlevel = runlevel;
     int was_in_eval = in_eval;
     HV *stash;
     GV *gv;
     CV *cv;
 
+    DEBUG_L(fprintf(stderr, "die: curstack = %p, mainstack= %p\n",
+                   curstack, mainstack));/*debug*/
     /* We have to switch back to mainstack or die_where may try to pop
      * the eval block from the wrong stack if die is being called from a
      * signal handler.  - dkindred@cs.cmu.edu */
@@ -1181,6 +1190,8 @@ die(pat, va_alist)
     message = mess(pat, &args);
     va_end(args);
 
+    DEBUG_L(fprintf(stderr, "die: message = %s\ndiehook = %p\n",
+                  message, diehook));/*debug*/
     if (diehook) {
        /* sv_2cv might call croak() */
        SV *olddiehook = diehook;
@@ -1208,6 +1219,9 @@ die(pat, va_alist)
     }
 
     restartop = die_where(message);
+    DEBUG_L(fprintf(stderr,
+                   "die: restartop = %p, was_in_eval = %d, oldrunlevel = %d\n",
+                   restartop, was_in_eval, oldrunlevel));/*debug*/
     if ((!restartop && was_in_eval) || oldrunlevel > 1)
        JMPENV_JUMP(3);
     return restartop;
@@ -1224,6 +1238,7 @@ croak(pat, va_alist)
     va_dcl
 #endif
 {
+    dTHR;
     va_list args;
     char *message;
     HV *stash;
@@ -1237,6 +1252,9 @@ croak(pat, va_alist)
 #endif
     message = mess(pat, &args);
     va_end(args);
+#ifdef USE_THREADS
+    DEBUG_L(fprintf(stderr, "croak: 0x%lx %s", (unsigned long) thr, message));
+#endif /* USE_THREADS */
     if (diehook) {
        /* sv_2cv might call croak() */
        SV *olddiehook = diehook;
@@ -1297,6 +1315,7 @@ warn(pat,va_alist)
 
     if (warnhook) {
        /* sv_2cv might call warn() */
+       dTHR;
        SV *oldwarnhook = warnhook;
        ENTER;
        SAVESPTR(warnhook);
@@ -1350,6 +1369,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++;
@@ -1399,7 +1419,7 @@ char *nam, *val;
     STRLEN namlen = strlen(nam);
     STRLEN vallen = strlen(val ? val : "");
 
-    New(9040, envstr, namlen + vallen + 3, char);
+    New(904, envstr, namlen + vallen + 3, char);
     (void)sprintf(envstr,"%s=%s",nam,val);
     if (!vallen) {
         /* An attempt to delete the entry.
@@ -1455,6 +1475,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)
@@ -1795,15 +1830,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;
@@ -1934,6 +1977,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);
@@ -1944,7 +1992,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
@@ -1957,7 +2010,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 */
 
@@ -1970,7 +2027,7 @@ int flags;
 {
     SV *sv;
     SV** svp;
-    char spid[16];
+    char spid[TYPE_CHARS(int)];
 
     if (!pid)
        return -1;
@@ -2026,7 +2083,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);
@@ -2164,10 +2221,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++;
@@ -2180,16 +2234,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;
@@ -2245,6 +2299,134 @@ I32 *retlen;
     return retval;
 }
 
+#ifdef USE_THREADS
+#ifdef FAKE_THREADS
+/* Very simplistic scheduler for now */
+void
+schedule(void)
+{
+    thr = thr->next_run;
+}
+
+void
+perl_cond_init(cp)
+perl_cond *cp;
+{
+    *cp = 0;
+}
+
+void
+perl_cond_signal(cp)
+perl_cond *cp;
+{
+    perl_thread t;
+    perl_cond cond = *cp;
+    
+    if (!cond)
+       return;
+    t = cond->thread;
+    /* Insert t in the runnable queue just ahead of us */
+    t->next_run = thr->next_run;
+    thr->next_run->prev_run = t;
+    t->prev_run = thr;
+    thr->next_run = t;
+    thr->wait_queue = 0;
+    /* Remove from the wait queue */
+    *cp = cond->next;
+    Safefree(cond);
+}
+
+void
+perl_cond_broadcast(cp)
+perl_cond *cp;
+{
+    perl_thread t;
+    perl_cond cond, cond_next;
+    
+    for (cond = *cp; cond; cond = cond_next) {
+       t = cond->thread;
+       /* Insert t in the runnable queue just ahead of us */
+       t->next_run = thr->next_run;
+       thr->next_run->prev_run = t;
+       t->prev_run = thr;
+       thr->next_run = t;
+       thr->wait_queue = 0;
+       /* Remove from the wait queue */
+       cond_next = cond->next;
+       Safefree(cond);
+    }
+    *cp = 0;
+}
+
+void
+perl_cond_wait(cp)
+perl_cond *cp;
+{
+    perl_cond cond;
+
+    if (thr->next_run == thr)
+       croak("panic: perl_cond_wait called by last runnable thread");
+    
+    New(666, cond, 1, struct perl_wait_queue);
+    cond->thread = thr;
+    cond->next = *cp;
+    *cp = cond;
+    thr->wait_queue = cond;
+    /* Remove ourselves from runnable queue */
+    thr->next_run->prev_run = thr->prev_run;
+    thr->prev_run->next_run = thr->next_run;
+}
+#endif /* FAKE_THREADS */
+
+#ifdef OLD_PTHREADS_API
+struct thread *
+getTHR _((void))
+{
+    pthread_addr_t t;
+
+    if (pthread_getspecific(thr_key, &t))
+       croak("panic: pthread_getspecific");
+    return (struct thread *) t;
+}
+#endif /* OLD_PTHREADS_API */
+
+MAGIC *
+condpair_magic(sv)
+SV *sv;
+{
+    MAGIC *mg;
+    
+    SvUPGRADE(sv, SVt_PVMG);
+    mg = mg_find(sv, 'm');
+    if (!mg) {
+       condpair_t *cp;
+
+       New(53, cp, 1, condpair_t);
+       MUTEX_INIT(&cp->mutex);
+       COND_INIT(&cp->owner_cond);
+       COND_INIT(&cp->cond);
+       cp->owner = 0;
+       MUTEX_LOCK(&sv_mutex);
+       mg = mg_find(sv, 'm');
+       if (mg) {
+           /* someone else beat us to initialising it */
+           MUTEX_UNLOCK(&sv_mutex);
+           MUTEX_DESTROY(&cp->mutex);
+           COND_DESTROY(&cp->owner_cond);
+           COND_DESTROY(&cp->cond);
+           Safefree(cp);
+       }
+       else {
+           sv_magic(sv, Nullsv, 'm', 0, 0);
+           mg = SvMAGIC(sv);
+           mg->mg_ptr = (char *)cp;
+           mg->mg_len = sizeof(cp);
+           MUTEX_UNLOCK(&sv_mutex);
+       }
+    }
+    return mg;
+}
+#endif /* USE_THREADS */
 
 #ifdef HUGE_VAL
 /*