aboutsummaryrefslogtreecommitdiffstats
path: root/strftime.c
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-03-23 11:57:01 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-03-23 11:57:01 +0000
commit8bc77e72eb806fca3be0f2293dc081e76803de4b (patch)
tree44c7235c2395f933342c2c6b269bc160c5ad5b55 /strftime.c
parentba154c2b46641edf479b4ac1a1bd99a79eafbbf0 (diff)
downloadruby-8bc77e72eb806fca3be0f2293dc081e76803de4b.tar.gz
strftime.c: format in String
* strftime.c (rb_strftime_with_timespec): append formatted results to the given string with expanding, and also deal with NUL chars. * strftime.c (rb_strftime, rb_strftime_timespec): return formatted string, not the length put in the given buffer. * time.c (rb_strftime_alloc): no longer needs to retry with reallocating buffers. * time.c (time_strftime): no longer needs to split by NUL chars. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@54236 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'strftime.c')
-rw-r--r--strftime.c147
1 files changed, 93 insertions, 54 deletions
diff --git a/strftime.c b/strftime.c
index 83550e9a7f..a426a455bd 100644
--- a/strftime.c
+++ b/strftime.c
@@ -50,6 +50,7 @@
#include "ruby/ruby.h"
#include "ruby/encoding.h"
#include "timev.h"
+#include "internal.h"
#ifndef GAWK
#include <stdio.h>
@@ -156,16 +157,35 @@ max(int a, int b)
/* strftime --- produce formatted time */
+static char *
+resize_buffer(VALUE ftime, char *s, const char **start, const char **endp,
+ ptrdiff_t n)
+{
+ size_t len = s - *start;
+ size_t nlen = len + n * 2;
+ rb_str_set_len(ftime, len);
+ rb_str_modify_expand(ftime, nlen-len);
+ s = RSTRING_PTR(ftime);
+ *endp = s + nlen;
+ *start = s;
+ return s += len;
+}
+
/*
* enc is the encoding of the format. It is used as the encoding of resulted
* string, but the name of the month and weekday are always US-ASCII. So it
* is only used for the timezone name on Windows.
*/
-static size_t
-rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, VALUE timev, struct timespec *ts, int gmt)
+static VALUE
+rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
+ rb_encoding *enc, const struct vtm *vtm, VALUE timev,
+ struct timespec *ts, int gmt)
{
- const char *const endp = s + maxsize;
- const char *const start = s;
+ size_t len = RSTRING_LEN(ftime);
+ char *s = RSTRING_PTR(ftime);
+ const char *start = s;
+ const char *endp = start + rb_str_capacity(ftime);
+ const char *const format_end = format + format_len;
const char *sp, *tp;
#define TBUFSIZE 100
auto char tbuf[TBUFSIZE];
@@ -193,27 +213,28 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
};
static const char ampm[][3] = { "AM", "PM", };
- if (s == NULL || format == NULL || vtm == NULL || maxsize == 0)
- return 0;
-
- /* quick check if we even need to bother */
- if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) {
+ if (format == NULL || format_len == 0 || vtm == NULL) {
err:
- errno = ERANGE;
return 0;
}
- if (enc && (enc == rb_usascii_encoding() ||
- enc == rb_ascii8bit_encoding() || enc == rb_locale_encoding())) {
- enc = NULL;
+ if (enc &&
+ (enc == rb_usascii_encoding() ||
+ enc == rb_ascii8bit_encoding() ||
+ enc == rb_locale_encoding())) {
+ enc = NULL;
}
- for (; *format && s < endp - 1; format++) {
+ s += len;
+ for (; format < format_end; format++) {
#define FLAG_FOUND() do { \
if (precision > 0) \
goto unknown; \
} while (0)
-#define NEEDS(n) do if (s >= endp || (n) >= endp - s - 1) goto err; while (0)
+#define NEEDS(n) do { \
+ if (s >= endp || (n) >= endp - s - 1) \
+ s = resize_buffer(ftime, s, &start, &endp, (n)); \
+ } while (0)
#define FILL_PADDING(i) do { \
if (!(flags & BIT_OF(LEFT)) && precision > (i)) { \
NEEDS(precision); \
@@ -226,25 +247,34 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
} while (0);
#define FMT(def_pad, def_prec, fmt, val) \
do { \
- int l; \
if (precision <= 0) precision = (def_prec); \
if (flags & BIT_OF(LEFT)) precision = 1; \
- l = snprintf(s, endp - s, \
- ((padding == '0' || (!padding && (def_pad) == '0')) ? "%0*"fmt : "%*"fmt), \
- precision, (val)); \
- if (l < 0) goto err; \
- s += l; \
+ len = s - start; \
+ NEEDS(precision); \
+ rb_str_set_len(ftime, len); \
+ rb_str_catf(ftime, \
+ ((padding == '0' || (!padding && (def_pad) == '0')) ? "%0*"fmt : "%*"fmt), \
+ precision, (val)); \
+ RSTRING_GETMEM(ftime, s, len); \
+ endp = (start = s) + rb_str_capacity(ftime); \
+ s += len; \
} while (0)
#define STRFTIME(fmt) \
do { \
- i = rb_strftime_with_timespec(s, endp - s, (fmt), enc, vtm, timev, ts, gmt); \
- if (!i) return 0; \
+ len = s - start; \
+ rb_str_set_len(ftime, len); \
+ if (!rb_strftime_with_timespec(ftime, (fmt), rb_strlen_lit(fmt), enc, vtm, timev, ts, gmt)) \
+ return 0; \
+ s = RSTRING_PTR(ftime); \
+ i = RSTRING_LEN(ftime) - len; \
+ endp = (start = s) + rb_str_capacity(ftime); \
+ s += len; \
if (precision > i) {\
NEEDS(precision); \
memmove(s + precision - i, s, i);\
memset(s, padding ? padding : ' ', precision - i); \
s += precision; \
- }\
+ } \
else s += i; \
} while (0)
#define FMTV(def_pad, def_prec, fmt, val) \
@@ -271,10 +301,14 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
} \
} while (0)
- if (*format != '%') {
- *s++ = *format;
- continue;
- }
+ tp = memchr(format, '%', format_end - format);
+ if (!tp) tp = format_end;
+ NEEDS(tp - format);
+ memcpy(s, format, tp - format);
+ s += tp - format;
+ format = tp;
+ if (format == format_end) break;
+
tp = tbuf;
sp = format;
precision = -1;
@@ -282,11 +316,8 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
padding = 0;
colons = 0;
again:
- switch (*++format) {
- case '\0':
- format--;
- goto unknown;
-
+ if (++format >= format_end) goto unknown;
+ switch (*format) {
case '%':
FILL_PADDING(1);
*s++ = '%';
@@ -768,12 +799,12 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
goto again;
case ':':
- {
- size_t l = strspn(format, ":");
- if (l > 3 || format[l] != 'z') goto unknown;
- colons = (int)l;
- format += l - 1;
+ for (colons = 1; colons <= 3; ++colons) {
+ if (format+colons >= format_end) goto unknown;
+ if (format[colons] == 'z') break;
+ if (format[colons] != ':') goto unknown;
}
+ format += colons - 1;
goto again;
case '0':
@@ -781,9 +812,12 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
- char *e;
- precision = (int)strtoul(format, &e, 10);
- format = e - 1;
+ size_t n;
+ int ov;
+ unsigned long u = ruby_scan_digits(format, format_end-format, 10, &n, &ov);
+ if (ov || u > INT_MAX) goto unknown;
+ precision = (int)u;
+ format += n - 1;
goto again;
}
@@ -817,26 +851,31 @@ rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encodi
}
}
}
- if (s >= endp) {
- goto err;
- }
- if (*format == '\0') {
- *s = '\0';
- return (s - start);
- } else
+ if (s >= endp || format != format_end) {
return 0;
+ }
+ len = s - start;
+ rb_str_set_len(ftime, len);
+ rb_str_resize(ftime, len);
+ return ftime;
}
-size_t
-rb_strftime(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, VALUE timev, int gmt)
+VALUE
+rb_strftime(const char *format, size_t format_len,
+ rb_encoding *enc, const struct vtm *vtm, VALUE timev, int gmt)
{
- return rb_strftime_with_timespec(s, maxsize, format, enc, vtm, timev, NULL, gmt);
+ VALUE result = rb_enc_str_new(0, 0, enc);
+ return rb_strftime_with_timespec(result, format, format_len, enc,
+ vtm, timev, NULL, gmt);
}
-size_t
-rb_strftime_timespec(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, struct timespec *ts, int gmt)
+VALUE
+rb_strftime_timespec(const char *format, size_t format_len,
+ rb_encoding *enc, const struct vtm *vtm, struct timespec *ts, int gmt)
{
- return rb_strftime_with_timespec(s, maxsize, format, enc, vtm, Qnil, ts, gmt);
+ VALUE result = rb_enc_str_new(0, 0, enc);
+ return rb_strftime_with_timespec(result, format, format_len, enc,
+ vtm, Qnil, ts, gmt);
}
/* isleap --- is a year a leap year? */