From wdunlap at tibco.com  Thu Mar 19 00:00:51 2009
From: wdunlap at tibco.com (William Dunlap)
Date: Wed, 18 Mar 2009 16:00:51 -0700
Subject: [Rd] sprintf("%d", integer(0)) aborts
Message-ID: <77EB52C6DD32BA4D87471DCD70C8D700DF1A0E@NA-PA-VBE03.na.tibco.com>

In R's sprintf() if any of the arguments has length 0
the function aborts.  E.g.,

   > sprintf("%d", integer(0))
   Error in sprintf("%d", integer(0)) : zero-length argument
   > sprintf(character(), integer(0))
   Error in sprintf(character(), integer(0)) :
     'fmt' is not a non-empty character vector

This comes up in code like
   x[nchar(x)==0] <- sprintf("No. %d", seq_along(x)[nchar(x)==0])
which works if x contains any empty strings
   x<-c("One","Two","") # changes "" -> "No. 3"
but not if it doesn't
   x<-c("One","Two","Three") # throws error instead of doing nothing

When I wrote S+'s sprintf() I had it act like the binary
arithmetic operators, returning a zero long result if any
argument were zero long.  (Otherwise its result is as long
as the longest input.)  I think it would be nice if R's
sprintf did this also.

Currently you must add defensive code (if (any(nchar(x)==0))...)
to make functions using sprintf to work in all cases and that
muddies up the code and slows things down.

Do you think this is a reasonable thing to do?  I've attached
a possible patch to src/main/sprintf.c makes the examples above
return character(0).

Bill Dunlap
TIBCO Software Inc - Spotfire Division
wdunlap tibco.com 

-------------------------------------------------------------------

Index: sprintf.c
===================================================================
--- sprintf.c   (revision 48148)
+++ sprintf.c   (working copy)
@@ -79,13 +79,13 @@
     static R_StringBuffer outbuff = {NULL, 0, MAXELTSIZE};
     Rboolean use_UTF8;

-    outputString = R_AllocStringBuffer(0, &outbuff);
-
     /* grab the format string */
     nargs = length(args);
     format = CAR(args);
-    if (!isString(format) || length(format) == 0)
+    if (!isString(format))
        error(_("'fmt' is not a non-empty character vector"));
+    if (length(format) == 0)
+       return allocVector(STRSXP, 0) ;
     args = CDR(args); nargs--;
     if(nargs >= 100)
        error(_("only 100 arguments are allowed"));
@@ -97,9 +97,12 @@
     for(i = 0; i < nargs; i++) {
        lens[i] = length(a[i]);
        if(lens[i] == 0)
-           error(_("zero-length argument"));
+           return allocVector(STRSXP, 0) ;
        if(maxlen < lens[i]) maxlen = lens[i];
     }
+
+    outputString = R_AllocStringBuffer(0, &outbuff);
+
     if(maxlen % length(format))
        error(_("arguments cannot be recycled to the same length"));
     for(i = 0; i < nargs; i++)


[Rd] sprintf("%d", integer(0)) aborts
William Dunlap