From 5396d8a1ab52b4da4f5199109472fe7f8ea43085 Mon Sep 17 00:00:00 2001 From: nobu Date: Wed, 23 Mar 2016 11:57:01 +0000 Subject: 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 --- strftime.c | 147 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 54 deletions(-) (limited to 'strftime.c') 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 @@ -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? */ -- cgit v1.2.1