Implement Cwd::abs_path in XS
[p5sagit/p5-mst-13.2.git] / util.c
diff --git a/util.c b/util.c
index 542d0dd..99470a7 100644 (file)
--- a/util.c
+++ b/util.c
@@ -4156,7 +4156,7 @@ char *tzname[] = { "" , "" };
 #endif
 
 void
-init_tm(struct tm *ptm)                /* see mktime, strftime and asctime     */
+Perl_init_tm(pTHX_ struct tm *ptm)     /* see mktime, strftime and asctime */
 {
 #ifdef STRUCT_TM_HASZONE
     Time_t now;
@@ -4170,7 +4170,7 @@ init_tm(struct tm *ptm)           /* see mktime, strftime and asctime     */
  * semantics (and overhead) of mktime().
  */
 void
-mini_mktime(struct tm *ptm)
+Perl_mini_mktime(pTHX_ struct tm *ptm)
 {
     int yearday;
     int secs;
@@ -4361,3 +4361,69 @@ mini_mktime(struct tm *ptm)
     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
+}
+