Re: stability of sort()?
[p5sagit/p5-mst-13.2.git] / util.c
diff --git a/util.c b/util.c
index c5a3af3..d33af51 100644 (file)
--- a/util.c
+++ b/util.c
@@ -577,18 +577,18 @@ Perl_set_numeric_radix(pTHX)
     lc = localeconv();
     if (lc && lc->decimal_point) {
        if (lc->decimal_point[0] == '.' && lc->decimal_point[1] == 0) {
-           SvREFCNT_dec(PL_numeric_radix);
-           PL_numeric_radix = 0;
+           SvREFCNT_dec(PL_numeric_radix_sv);
+           PL_numeric_radix_sv = Nullsv;
        }
        else {
-           if (PL_numeric_radix)
-               sv_setpv(PL_numeric_radix, lc->decimal_point);
+           if (PL_numeric_radix_sv)
+               sv_setpv(PL_numeric_radix_sv, lc->decimal_point);
            else
-               PL_numeric_radix = newSVpv(lc->decimal_point, 0);
+               PL_numeric_radix_sv = newSVpv(lc->decimal_point, 0);
        }
     }
     else
-       PL_numeric_radix = 0;
+       PL_numeric_radix_sv = Nullsv;
 # endif /* HAS_LOCALECONV */
 #endif /* USE_LOCALE_NUMERIC */
 }
@@ -1033,7 +1033,7 @@ Perl_fbm_compile(pTHX_ SV *sv, U32 flags)
            s--, i++;
        }
     }
-    sv_magic(sv, Nullsv, 'B', Nullch, 0);      /* deep magic */
+    sv_magic(sv, Nullsv, PERL_MAGIC_bm, Nullch, 0);    /* deep magic */
     SvVALID_on(sv);
 
     s = (unsigned char*)(SvPVX(sv));           /* deeper magic */
@@ -2923,80 +2923,74 @@ Perl_repeatcpy(pTHX_ register char *to, register const char *from, I32 len, regi
 U32
 Perl_cast_ulong(pTHX_ NV f)
 {
-    long along;
-
+  if (f < 0.0)
+    return f < I32_MIN ? (U32) I32_MIN : (U32)(I32) f;
+  if (f < U32_MAX_P1) {
 #if CASTFLAGS & 2
-#   define BIGDOUBLE 2147483648.0
-    if (f >= BIGDOUBLE)
-       return (unsigned long)(f-(long)(f/BIGDOUBLE)*BIGDOUBLE)|0x80000000;
+    if (f < U32_MAX_P1_HALF)
+      return (U32) f;
+    f -= U32_MAX_P1_HALF;
+    return ((U32) f) | (1 + U32_MAX >> 1);
+#else
+    return (U32) f;
 #endif
-    if (f >= 0.0)
-       return (unsigned long)f;
-    along = (long)f;
-    return (unsigned long)along;
+  }
+  return f > 0 ? U32_MAX : 0 /* NaN */;
 }
-# undef BIGDOUBLE
-
-/* Unfortunately, on some systems the cast_uv() function doesn't
-   work with the system-supplied definition of ULONG_MAX.  The
-   comparison  (f >= ULONG_MAX) always comes out true.  It must be a
-   problem with the compiler constant folding.
-
-   In any case, this workaround should be fine on any two's complement
-   system.  If it's not, supply a '-DMY_ULONG_MAX=whatever' in your
-   ccflags.
-              --Andy Dougherty      <doughera@lafcol.lafayette.edu>
-*/
-
-/* Code modified to prefer proper named type ranges, I32, IV, or UV, instead
-   of LONG_(MIN/MAX).
-                           -- Kenneth Albanowski <kjahds@kjahds.com>
-*/
-
-#ifndef MY_UV_MAX
-#  define MY_UV_MAX ((UV)IV_MAX * (UV)2 + (UV)1)
-#endif
 
 I32
 Perl_cast_i32(pTHX_ NV f)
 {
-    if (f >= I32_MAX)
-       return (I32) I32_MAX;
-    if (f <= I32_MIN)
-       return (I32) I32_MIN;
-    return (I32) f;
+  if (f < I32_MAX_P1)
+    return f < I32_MIN ? I32_MIN : (I32) f;
+  if (f < U32_MAX_P1) {
+#if CASTFLAGS & 2
+    if (f < U32_MAX_P1_HALF)
+      return (I32)(U32) f;
+    f -= U32_MAX_P1_HALF;
+    return (I32)(((U32) f) | (1 + U32_MAX >> 1));
+#else
+    return (I32)(U32) f;
+#endif
+  }
+  return f > 0 ? (I32)U32_MAX : 0 /* NaN */;
 }
 
 IV
 Perl_cast_iv(pTHX_ NV f)
 {
-    if (f >= IV_MAX) {
-       UV uv;
-       
-       if (f >= (NV)UV_MAX)
-           return (IV) UV_MAX; 
-       uv = (UV) f;
-       return (IV)uv;
-    }
-    if (f <= IV_MIN)
-       return (IV) IV_MIN;
-    return (IV) f;
+  if (f < IV_MAX_P1)
+    return f < IV_MIN ? IV_MIN : (IV) f;
+  if (f < UV_MAX_P1) {
+#if CASTFLAGS & 2
+    /* For future flexibility allowing for sizeof(UV) >= sizeof(IV)  */
+    if (f < UV_MAX_P1_HALF)
+      return (IV)(UV) f;
+    f -= UV_MAX_P1_HALF;
+    return (IV)(((UV) f) | (1 + UV_MAX >> 1));
+#else
+    return (IV)(UV) f;
+#endif
+  }
+  return f > 0 ? (IV)UV_MAX : 0 /* NaN */;
 }
 
 UV
 Perl_cast_uv(pTHX_ NV f)
 {
-    if (f >= MY_UV_MAX)
-       return (UV) MY_UV_MAX;
-    if (f < 0) {
-       IV iv;
-       
-       if (f < IV_MIN)
-           return (UV)IV_MIN;
-       iv = (IV) f;
-       return (UV) iv;
-    }
+  if (f < 0.0)
+    return f < IV_MIN ? (UV) IV_MIN : (UV)(IV) f;
+  if (f < UV_MAX_P1) {
+#if CASTFLAGS & 2
+    if (f < UV_MAX_P1_HALF)
+      return (UV) f;
+    f -= UV_MAX_P1_HALF;
+    return ((UV) f) | (1 + UV_MAX >> 1);
+#else
     return (UV) f;
+#endif
+  }
+  return f > 0 ? UV_MAX : 0 /* NaN */;
 }
 
 #ifndef HAS_RENAME
@@ -3489,11 +3483,11 @@ Perl_get_context(void)
        Perl_croak_nocontext("panic: pthread_getspecific");
     return (void*)t;
 #  else
-#  ifdef I_MACH_CTHREADS
+#    ifdef I_MACH_CTHREADS
     return (void*)cthread_data(cthread_self());
-#  else
-    return (void*)pthread_getspecific(PL_thr_key);
-#  endif
+#    else
+    return (void*)PTHREAD_GETSPECIFIC(PL_thr_key);
+#    endif
 #  endif
 #else
     return (void*)NULL;
@@ -3597,7 +3591,7 @@ Perl_condpair_magic(pTHX_ SV *sv)
     MAGIC *mg;
 
     SvUPGRADE(sv, SVt_PVMG);
-    mg = mg_find(sv, 'm');
+    mg = mg_find(sv, PERL_MAGIC_mutex);
     if (!mg) {
        condpair_t *cp;
 
@@ -3607,7 +3601,7 @@ Perl_condpair_magic(pTHX_ SV *sv)
        COND_INIT(&cp->cond);
        cp->owner = 0;
        LOCK_CRED_MUTEX;                /* XXX need separate mutex? */
-       mg = mg_find(sv, 'm');
+       mg = mg_find(sv, PERL_MAGIC_mutex);
        if (mg) {
            /* someone else beat us to initialising it */
            UNLOCK_CRED_MUTEX;          /* XXX need separate mutex? */
@@ -3617,7 +3611,7 @@ Perl_condpair_magic(pTHX_ SV *sv)
            Safefree(cp);
        }
        else {
-           sv_magic(sv, Nullsv, 'm', 0, 0);
+           sv_magic(sv, Nullsv, PERL_MAGIC_mutex, 0, 0);
            mg = SvMAGIC(sv);
            mg->mg_ptr = (char *)cp;
            mg->mg_len = sizeof(cp);
@@ -3761,7 +3755,7 @@ Perl_new_struct_thread(pTHX_ struct perl_thread *t)
        if (*svp && *svp != &PL_sv_undef) {
            SV *sv = newSVsv(*svp);
            av_store(thr->threadsv, i, sv);
-           sv_magic(sv, 0, 0, &PL_threadsv_names[i], 1);
+           sv_magic(sv, 0, PERL_MAGIC_sv, &PL_threadsv_names[i], 1);
            DEBUG_S(PerlIO_printf(Perl_debug_log,
                "new_struct_thread: copied threadsv %"IVdf" %p->%p\n",
                                  (IV)i, t, thr));
@@ -4040,11 +4034,12 @@ Perl_report_evil_fh(pTHX_ GV *gv, IO *io, I32 op)
        op == OP_LEAVEWRITE ? "write" :         /* "write exit" not nice */
        PL_op_desc[op];
     char *pars = OP_IS_FILETEST(op) ? "" : "()";
-    char *type = OP_IS_SOCKET(op) || (io && IoTYPE(io) == IoTYPE_SOCKET) ?
+    char *type = OP_IS_SOCKET(op) ||
+                 (gv && io && IoTYPE(io) == IoTYPE_SOCKET) ?
                      "socket" : "filehandle";
     char *name = NULL;
 
-    if (io && IoTYPE(io) == IoTYPE_CLOSED) {
+    if (gv && io && IoTYPE(io) == IoTYPE_CLOSED) {
        vile = "closed";
        warn_type = WARN_CLOSED;
     }
@@ -4078,7 +4073,7 @@ Perl_report_evil_fh(pTHX_ GV *gv, IO *io, I32 op)
     else {
        Perl_warner(aTHX_ warn_type,
                    "%s%s on %s %s", func, pars, vile, type);
-       if (io && IoDIRP(io) && !(IoFLAGS(io) & IOf_FAKE_DIRP))
+       if (gv && io && IoDIRP(io) && !(IoFLAGS(io) & IOf_FAKE_DIRP))
            Perl_warner(aTHX_ warn_type,
                        "\t(Are you trying to call %s%s on dirhandle?)\n",
                        func, pars);
@@ -4124,3 +4119,296 @@ Perl_ebcdic_control(pTHX_ int ch)
        }
 }
 #endif
+
+/* XXX struct tm on some systems (SunOS4/BSD) contains extra (non POSIX)
+ * fields for which we don't have Configure support yet:
+ *   char *tm_zone;   -- abbreviation of timezone name
+ *   long tm_gmtoff;  -- offset from GMT in seconds
+ * To workaround core dumps from the uninitialised tm_zone we get the
+ * system to give us a reasonable struct to copy.  This fix means that
+ * strftime uses the tm_zone and tm_gmtoff values returned by
+ * localtime(time()). That should give the desired result most of the
+ * time. But probably not always!
+ *
+ * This is a temporary workaround to be removed once Configure
+ * support is added and NETaa14816 is considered in full.
+ * It does not address tzname aspects of NETaa14816.
+ */
+#ifdef HAS_GNULIBC
+# ifndef STRUCT_TM_HASZONE
+#    define STRUCT_TM_HASZONE
+# endif
+#endif
+
+void
+Perl_init_tm(pTHX_ struct tm *ptm)     /* see mktime, strftime and asctime */
+{
+#ifdef STRUCT_TM_HASZONE
+    Time_t now;
+    (void)time(&now);
+    Copy(localtime(&now), ptm, 1, struct tm);
+#endif
+}
+
+/*
+ * mini_mktime - normalise struct tm values without the localtime()
+ * semantics (and overhead) of mktime().
+ */
+void
+Perl_mini_mktime(pTHX_ struct tm *ptm)
+{
+    int yearday;
+    int secs;
+    int month, mday, year, jday;
+    int odd_cent, odd_year;
+
+#define        DAYS_PER_YEAR   365
+#define        DAYS_PER_QYEAR  (4*DAYS_PER_YEAR+1)
+#define        DAYS_PER_CENT   (25*DAYS_PER_QYEAR-1)
+#define        DAYS_PER_QCENT  (4*DAYS_PER_CENT+1)
+#define        SECS_PER_HOUR   (60*60)
+#define        SECS_PER_DAY    (24*SECS_PER_HOUR)
+/* parentheses deliberately absent on these two, otherwise they don't work */
+#define        MONTH_TO_DAYS   153/5
+#define        DAYS_TO_MONTH   5/153
+/* offset to bias by March (month 4) 1st between month/mday & year finding */
+#define        YEAR_ADJUST     (4*MONTH_TO_DAYS+1)
+/* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */
+#define        WEEKDAY_BIAS    6       /* (1+6)%7 makes Sunday 0 again */
+
+/*
+ * Year/day algorithm notes:
+ *
+ * With a suitable offset for numeric value of the month, one can find
+ * an offset into the year by considering months to have 30.6 (153/5) days,
+ * using integer arithmetic (i.e., with truncation).  To avoid too much
+ * messing about with leap days, we consider January and February to be
+ * the 13th and 14th month of the previous year.  After that transformation,
+ * we need the month index we use to be high by 1 from 'normal human' usage,
+ * so the month index values we use run from 4 through 15.
+ *
+ * Given that, and the rules for the Gregorian calendar (leap years are those
+ * divisible by 4 unless also divisible by 100, when they must be divisible
+ * by 400 instead), we can simply calculate the number of days since some
+ * arbitrary 'beginning of time' by futzing with the (adjusted) year number,
+ * the days we derive from our month index, and adding in the day of the
+ * month.  The value used here is not adjusted for the actual origin which
+ * it normally would use (1 January A.D. 1), since we're not exposing it.
+ * We're only building the value so we can turn around and get the
+ * normalised values for the year, month, day-of-month, and day-of-year.
+ *
+ * For going backward, we need to bias the value we're using so that we find
+ * the right year value.  (Basically, we don't want the contribution of
+ * March 1st to the number to apply while deriving the year).  Having done
+ * that, we 'count up' the contribution to the year number by accounting for
+ * full quadracenturies (400-year periods) with their extra leap days, plus
+ * the contribution from full centuries (to avoid counting in the lost leap
+ * days), plus the contribution from full quad-years (to count in the normal
+ * leap days), plus the leftover contribution from any non-leap years.
+ * At this point, if we were working with an actual leap day, we'll have 0
+ * days left over.  This is also true for March 1st, however.  So, we have
+ * to special-case that result, and (earlier) keep track of the 'odd'
+ * century and year contributions.  If we got 4 extra centuries in a qcent,
+ * or 4 extra years in a qyear, then it's a leap day and we call it 29 Feb.
+ * Otherwise, we add back in the earlier bias we removed (the 123 from
+ * figuring in March 1st), find the month index (integer division by 30.6),
+ * and the remainder is the day-of-month.  We then have to convert back to
+ * 'real' months (including fixing January and February from being 14/15 in
+ * the previous year to being in the proper year).  After that, to get
+ * tm_yday, we work with the normalised year and get a new yearday value for
+ * January 1st, which we subtract from the yearday value we had earlier,
+ * representing the date we've re-built.  This is done from January 1
+ * because tm_yday is 0-origin.
+ *
+ * Since POSIX time routines are only guaranteed to work for times since the
+ * UNIX epoch (00:00:00 1 Jan 1970 UTC), the fact that this algorithm
+ * applies Gregorian calendar rules even to dates before the 16th century
+ * doesn't bother me.  Besides, you'd need cultural context for a given
+ * date to know whether it was Julian or Gregorian calendar, and that's
+ * outside the scope for this routine.  Since we convert back based on the
+ * same rules we used to build the yearday, you'll only get strange results
+ * for input which needed normalising, or for the 'odd' century years which
+ * were leap years in the Julian calander but not in the Gregorian one.
+ * I can live with that.
+ *
+ * This algorithm also fails to handle years before A.D. 1 gracefully, but
+ * that's still outside the scope for POSIX time manipulation, so I don't
+ * care.
+ */
+
+    year = 1900 + ptm->tm_year;
+    month = ptm->tm_mon;
+    mday = ptm->tm_mday;
+    /* allow given yday with no month & mday to dominate the result */
+    if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
+       month = 0;
+       mday = 0;
+       jday = 1 + ptm->tm_yday;
+    }
+    else {
+       jday = 0;
+    }
+    if (month >= 2)
+       month+=2;
+    else
+       month+=14, year--;
+    yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
+    yearday += month*MONTH_TO_DAYS + mday + jday;
+    /*
+     * Note that we don't know when leap-seconds were or will be,
+     * so we have to trust the user if we get something which looks
+     * like a sensible leap-second.  Wild values for seconds will
+     * be rationalised, however.
+     */
+    if ((unsigned) ptm->tm_sec <= 60) {
+       secs = 0;
+    }
+    else {
+       secs = ptm->tm_sec;
+       ptm->tm_sec = 0;
+    }
+    secs += 60 * ptm->tm_min;
+    secs += SECS_PER_HOUR * ptm->tm_hour;
+    if (secs < 0) {
+       if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
+           /* got negative remainder, but need positive time */
+           /* back off an extra day to compensate */
+           yearday += (secs/SECS_PER_DAY)-1;
+           secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
+       }
+       else {
+           yearday += (secs/SECS_PER_DAY);
+           secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
+       }
+    }
+    else if (secs >= SECS_PER_DAY) {
+       yearday += (secs/SECS_PER_DAY);
+       secs %= SECS_PER_DAY;
+    }
+    ptm->tm_hour = secs/SECS_PER_HOUR;
+    secs %= SECS_PER_HOUR;
+    ptm->tm_min = secs/60;
+    secs %= 60;
+    ptm->tm_sec += secs;
+    /* done with time of day effects */
+    /*
+     * The algorithm for yearday has (so far) left it high by 428.
+     * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
+     * bias it by 123 while trying to figure out what year it
+     * really represents.  Even with this tweak, the reverse
+     * translation fails for years before A.D. 0001.
+     * It would still fail for Feb 29, but we catch that one below.
+     */
+    jday = yearday;    /* save for later fixup vis-a-vis Jan 1 */
+    yearday -= YEAR_ADJUST;
+    year = (yearday / DAYS_PER_QCENT) * 400;
+    yearday %= DAYS_PER_QCENT;
+    odd_cent = yearday / DAYS_PER_CENT;
+    year += odd_cent * 100;
+    yearday %= DAYS_PER_CENT;
+    year += (yearday / DAYS_PER_QYEAR) * 4;
+    yearday %= DAYS_PER_QYEAR;
+    odd_year = yearday / DAYS_PER_YEAR;
+    year += odd_year;
+    yearday %= DAYS_PER_YEAR;
+    if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
+       month = 1;
+       yearday = 29;
+    }
+    else {
+       yearday += YEAR_ADJUST; /* recover March 1st crock */
+       month = yearday*DAYS_TO_MONTH;
+       yearday -= month*MONTH_TO_DAYS;
+       /* recover other leap-year adjustment */
+       if (month > 13) {
+           month-=14;
+           year++;
+       }
+       else {
+           month-=2;
+       }
+    }
+    ptm->tm_year = year - 1900;
+    if (yearday) {
+      ptm->tm_mday = yearday;
+      ptm->tm_mon = month;
+    }
+    else {
+      ptm->tm_mday = 31;
+      ptm->tm_mon = month - 1;
+    }
+    /* re-build yearday based on Jan 1 to get tm_yday */
+    year--;
+    yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
+    yearday += 14*MONTH_TO_DAYS + 1;
+    ptm->tm_yday = jday - yearday;
+    /* fix tm_wday if not overridden by caller */
+    if ((unsigned)ptm->tm_wday > 6)
+       ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
+}
+
+char *
+Perl_my_strftime(pTHX_ char *fmt, int sec, int min, int hour, int mday, int mon, int year, int wday, int yday, int isdst)
+{
+#ifdef HAS_STRFTIME
+  char *buf;
+  int buflen;
+  struct tm mytm;
+  int len;
+
+  init_tm(&mytm);      /* XXX workaround - see init_tm() above */
+  mytm.tm_sec = sec;
+  mytm.tm_min = min;
+  mytm.tm_hour = hour;
+  mytm.tm_mday = mday;
+  mytm.tm_mon = mon;
+  mytm.tm_year = year;
+  mytm.tm_wday = wday;
+  mytm.tm_yday = yday;
+  mytm.tm_isdst = isdst;
+  mini_mktime(&mytm);
+  buflen = 64;
+  New(0, buf, buflen, char);
+  len = strftime(buf, buflen, fmt, &mytm);
+  /*
+  ** The following is needed to handle to the situation where 
+  ** tmpbuf overflows.  Basically we want to allocate a buffer
+  ** and try repeatedly.  The reason why it is so complicated
+  ** is that getting a return value of 0 from strftime can indicate
+  ** one of the following:
+  ** 1. buffer overflowed,
+  ** 2. illegal conversion specifier, or
+  ** 3. the format string specifies nothing to be returned(not
+  **     an error).  This could be because format is an empty string
+  **    or it specifies %p that yields an empty string in some locale.
+  ** If there is a better way to make it portable, go ahead by
+  ** all means.
+  */
+  if ((len > 0 && len < buflen) || (len == 0 && *fmt == '\0'))
+    return buf;
+  else {
+    /* Possibly buf overflowed - try again with a bigger buf */
+    int     fmtlen = strlen(fmt);
+    int            bufsize = fmtlen + buflen;
+    
+    New(0, buf, bufsize, char);
+    while (buf) {
+      buflen = strftime(buf, bufsize, fmt, &mytm);
+      if (buflen > 0 && buflen < bufsize)
+       break;
+      /* heuristic to prevent out-of-memory errors */
+      if (bufsize > 100*fmtlen) {
+       Safefree(buf);
+       buf = NULL;
+       break;
+      }
+      bufsize *= 2;
+      Renew(buf, bufsize, char);
+    }
+    return buf;
+  }
+#else
+  Perl_croak(aTHX_ "panic: no strftime");
+#endif
+}
+