Skip to content

Commit 2e36b36

Browse files
author
maechler
committed
fix format.POSIXlt(<short TZ>) seg.fault (PR#16685)
git-svn-id: https://svn.r-project.org/R/trunk@70048 00db46b3-68df-0310-9c12-caf00c1e9a41
1 parent 3bfbd40 commit 2e36b36

File tree

4 files changed

+52
-14
lines changed

4 files changed

+52
-14
lines changed

doc/NEWS.Rd

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,9 +527,12 @@
527527
528528
\item \code{withCallingHandler()} now (again) handles warnings
529529
even during S4 generic's argument evaluation. (\PR{16111})
530-
531-
\item \code{deparse(..., control = "quoteExpressions")}
530+
531+
\item \code{deparse(..., control = "quoteExpressions")}
532532
incorrectly quoted empty expressions. (\PR{16686})
533+
534+
\item \code{format()}ting datetime objects (\code{"POSIX[cl]?t"})
535+
could segfault or recycle wrongly. (\PR{16685})
533536
}
534537
}
535538
}

src/library/base/R/datetime.R

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# File src/library/base/R/datetime.R
22
# Part of the R package, https://www.R-project.org
33
#
4-
# Copyright (C) 1995-2015 The R Core Team
4+
# Copyright (C) 1995-2016 The R Core Team
55
#
66
# This program is free software; you can redistribute it and/or modify
77
# it under the terms of the GNU General Public License as published by
@@ -162,21 +162,22 @@ length.POSIXlt <- function(x) length(x[[1L]])
162162
format.POSIXlt <- function(x, format = "", usetz = FALSE, ...)
163163
{
164164
if(!inherits(x, "POSIXlt")) stop("wrong class")
165-
if(format == "") {
165+
if(any(f0 <- format == "")) {
166166
## need list [ method here.
167-
times <- unlist(unclass(x)[1L:3L])
168-
secs <- x$sec; secs <- secs[!is.na(secs)]
167+
times <- unlist(unclass(x)[1L:3L])[f0]
168+
secs <- x$sec[f0]; secs <- secs[!is.na(secs)]
169169
np <- getOption("digits.secs")
170-
if(is.null(np)) np <- 0L else np <- min(6L, np)
170+
np <- if(is.null(np)) 0L else min(6L, np)
171171
if(np >= 1L)
172172
for (i in seq_len(np)- 1L)
173173
if(all( abs(secs - round(secs, i)) < 1e-6 )) {
174174
np <- i
175175
break
176176
}
177-
format <- if(all(times[!is.na(times)] == 0)) "%Y-%m-%d"
178-
else if(np == 0L) "%Y-%m-%d %H:%M:%S"
179-
else paste0("%Y-%m-%d %H:%M:%OS", np)
177+
format[f0] <-
178+
if(all(times[!is.na(times)] == 0)) "%Y-%m-%d"
179+
else if(np == 0L) "%Y-%m-%d %H:%M:%S"
180+
else paste0("%Y-%m-%d %H:%M:%OS", np)
180181
}
181182
## <FIXME>
182183
## Move names handling to C code eventually ...

src/main/datetime.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,12 @@ SEXP attribute_hidden do_formatPOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
909909
if(n > 0) N = (m > n) ? m:n; else N = 0;
910910
PROTECT(ans = allocVector(STRSXP, N));
911911
char tm_zone[20];
912-
Rboolean have_zone = LENGTH(x) >= 10 && LENGTH(VECTOR_ELT(x, 9)) == n;
912+
#ifdef HAVE_TM_GMTOFF
913+
Rboolean have_zone = LENGTH(x) >= 11 && XLENGTH(VECTOR_ELT(x, 9)) == n &&
914+
XLENGTH(VECTOR_ELT(x, 10)) == n;
915+
#else
916+
Rboolean have_zone = LENGTH(x) >= 10 && XLENGTH(VECTOR_ELT(x, 9)) == n;
917+
#endif
913918
for(R_xlen_t i = 0; i < N; i++) {
914919
double secs = REAL(VECTOR_ELT(x, 0))[i%nlen[0]], fsecs = floor(secs);
915920
// avoid (int) NAN
@@ -923,7 +928,7 @@ SEXP attribute_hidden do_formatPOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
923928
tm.tm_yday = INTEGER(VECTOR_ELT(x, 7))[i%nlen[7]];
924929
tm.tm_isdst = INTEGER(VECTOR_ELT(x, 8))[i%nlen[8]];
925930
if(have_zone) {
926-
strncpy(tm_zone, CHAR(STRING_ELT(VECTOR_ELT(x, 9), i)), 20);
931+
strncpy(tm_zone, CHAR(STRING_ELT(VECTOR_ELT(x, 9), i%n)), 20);
927932
tm_zone[20 - 1] = '\0';
928933
#ifdef HAVE_TM_ZONE
929934
tm.tm_zone = tm_zone;
@@ -999,8 +1004,8 @@ SEXP attribute_hidden do_formatPOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
9991004
// Now assume tzone abbreviated name is < 40 bytes,
10001005
// but they are currently 3 or 4 bytes.
10011006
if(UseTZ) {
1002-
if(LENGTH(x) >= 10) {
1003-
const char *p = CHAR(STRING_ELT(VECTOR_ELT(x, 9), i));
1007+
if(have_zone) {
1008+
const char *p = CHAR(STRING_ELT(VECTOR_ELT(x, 9), i%n));
10041009
if(strlen(p)) {strcat(buff, " "); strcat(buff, p);}
10051010
} else if(!isNull(tz)) {
10061011
int ii = 0;

tests/reg-tests-1c.R

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,3 +1294,32 @@ stopifnot(all.equal(coef(flm), cf[,"tear"]),
12941294
cbind(rate = 3:2, additive = 3:4,
12951295
`rate:additive` = c(3L, 8L))))
12961296
## dummy.coef() were missing coefficients in R <= 3.2.3
1297+
1298+
1299+
## format.POSIXlt() with modified 'zone' or length-2 format
1300+
f0 <- "2016-01-28 01:23:45"; tz0 <- "Europe/Stockholm"
1301+
d2 <- d1 <- rep(as.POSIXlt(f0, tz = tz0), 2)
1302+
f1 <- format(d1, usetz=TRUE)
1303+
d2$zone <- d1$zone[1] # length 1 instead of 2
1304+
f2 <- format(d2, usetz=TRUE)## -> segfault
1305+
f1.2 <- format(as.POSIXlt("2016-01-28 01:23:45"), format=c("%d", "%y"))# segfault
1306+
stopifnot(
1307+
identical(f1, rep(paste(f0, "CET"), 2)),
1308+
identical(f2, rep(paste(f0, tz0 ), 2)),
1309+
identical(f1.2, c("28", "16"))
1310+
)
1311+
tims <- seq.POSIXt(as.POSIXct("2016-01-01"),
1312+
as.POSIXct("2017-11-11"), by = as.difftime(pi, units="weeks"))
1313+
form <- c("%m/%d/%y %H:%M:%S", "", "%Y-%m-%d %H:%M:%S")
1314+
op <- options(warn = 2)# no warnings allowed
1315+
head(rf1 <- format(tims, form)) # recycling was wrong
1316+
head(rf2 <- format(tims, form[c(2,1,3)]))
1317+
stopifnot(identical(rf1[1:3], c("01/01/16 00:00:00", "2016-01-22 23:47:15",
1318+
"2016-02-13 23:34:30")),
1319+
identical(rf2[1:3], c("2016-01-01 00:00:00", "01/22/16 23:47:15",
1320+
rf1[3])),
1321+
nchar(rf1) == rep(c(17,19,19), length = length(rf1)),
1322+
nchar(rf2) == rep(c(19,17,19), length = length(rf2)))
1323+
options(op)
1324+
## Wrong-length 'zone' or short 'x' segfaulted -- PR#16685
1325+
## Default 'format' setting sometimes failed for length(format) > 1

0 commit comments

Comments
 (0)