The R Project SVN R

Rev

Rev 60844 | Rev 68923 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
19139 maechler 1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
62580 ripley 3
 *  Copyright (C) 2002--2013     The R Core Team
19139 maechler 4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
42307 ripley 16
 *  along with this program; if not, a copy is available at
17
 *  http://www.r-project.org/Licenses/
19139 maechler 18
 *
46947 ripley 19
 * Originally written by Jonathan Rougier
19139 maechler 20
*/
32380 ripley 21
 
19133 maechler 22
#ifdef HAVE_CONFIG_H
23
#include <config.h>
24
#endif
25
 
26
#include <Defn.h>
60667 ripley 27
#include <Internal.h>
41978 ripley 28
#include "RBufferUtils.h"
50894 ripley 29
#include <R_ext/RS.h> /* for Calloc/Free */
19133 maechler 30
 
31
#define MAXLINE MAXELTSIZE
48376 maechler 32
#define MAXNARGS 100
33
/*               ^^^ not entirely arbitrary, but strongly linked to allowing %$1 to %$99 !*/
19133 maechler 34
 
48376 maechler 35
/*
46947 ripley 36
   This is passed a format that started with % and may include other
37
   chars, e.g. '.2f abc'.  It's aim is to show that this is a valid
48376 maechler 38
   format from one of the types given in pattern.
46947 ripley 39
*/
40
 
41
static const char *findspec(const char *str)
42
{
46949 ripley 43
    /* This is not strict about checking where '.' is allowed.
48376 maechler 44
       It should allow  - + ' ' # 0 as flags
46949 ripley 45
       m m. .n n.m as width/precision
46
    */
46947 ripley 47
    const char *p = str;
48
 
49
    if(*p != '%') return p;
50
    for(p++; ; p++) {
48295 maechler 51
	if(*p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '.' ) continue;
46949 ripley 52
	/* '*' will currently have got substituted before this */
53
	if(*p == '*' || (*p >= '0' && *p <= '9')) continue;
46947 ripley 54
	break;
55
    }
56
    return p;
57
}
58
 
59
 
48376 maechler 60
/*   FALSE is success, TRUE is an error: pattern *not* found . */
46947 ripley 61
static Rboolean checkfmt(const char *fmt, const char *pattern)
62
{
63
    const char *p =fmt;
64
 
48376 maechler 65
    if(*p != '%') return TRUE;
46947 ripley 66
    p = findspec(fmt);
48376 maechler 67
    return strcspn(p, pattern) ? TRUE : FALSE;
46947 ripley 68
}
69
 
59226 ripley 70
#define TRANSLATE_CHAR(_STR_, _i_)  \
71
   ((use_UTF8) ? translateCharUTF8(STRING_ELT(_STR_, _i_))  \
72
    : translateChar(STRING_ELT(_STR_, _i_)))
46947 ripley 73
 
59226 ripley 74
 
36990 ripley 75
SEXP attribute_hidden do_sprintf(SEXP call, SEXP op, SEXP args, SEXP env)
19133 maechler 76
{
48378 maechler 77
    int i, nargs, cnt, v, thislen, nfmt, nprotect = 0;
41978 ripley 78
    /* fmt2 is a copy of fmt with '*' expanded.
42001 ripley 79
       bit will hold numeric formats and %<w>s, so be quite small. */
45446 ripley 80
    char fmt[MAXLINE+1], fmt2[MAXLINE+10], *fmtp, bit[MAXLINE+1],
59226 ripley 81
	*outputString;
82
    const char *formatString;
19133 maechler 83
    size_t n, cur, chunk;
84
 
48415 maechler 85
    SEXP format, _this, a[MAXNARGS], ans /* -Wall */ = R_NilValue;
48376 maechler 86
    int ns, maxlen, lens[MAXNARGS], nthis, nstar, star_arg = 0;
41978 ripley 87
    static R_StringBuffer outbuff = {NULL, 0, MAXELTSIZE};
48376 maechler 88
    Rboolean has_star, use_UTF8;
19133 maechler 89
 
48399 maechler 90
#define _my_sprintf(_X_)						\
91
    {									\
92
	int nc = snprintf(bit, MAXLINE+1, fmtp, _X_);			\
93
	if (nc > MAXLINE)						\
60844 ripley 94
	    error(_("required resulting string length %d is greater than maximal %d"), \
48399 maechler 95
		  nc, MAXLINE);						\
96
    }
97
 
48376 maechler 98
    nargs = length(args);
19133 maechler 99
    /* grab the format string */
100
    format = CAR(args);
48156 ripley 101
    if (!isString(format))
102
	error(_("'fmt' is not a character vector"));
48378 maechler 103
    nfmt = length(format);
104
    if (nfmt == 0) return allocVector(STRSXP, 0);
33117 ripley 105
    args = CDR(args); nargs--;
48376 maechler 106
    if(nargs >= MAXNARGS)
107
	error(_("only %d arguments are allowed"), MAXNARGS);
19133 maechler 108
 
44700 ripley 109
    /* record the args for possible coercion and later re-ordering */
48378 maechler 110
    for(i = 0; i < nargs; i++, args = CDR(args)) {
48415 maechler 111
	SEXPTYPE t_ai;
48378 maechler 112
	a[i] = CAR(args);
48415 maechler 113
	if((t_ai = TYPEOF(a[i])) == LANGSXP || t_ai == SYMSXP) /* << maybe add more .. */
114
	    error(_("invalid type of argument[%d]: '%s'"),
115
		  i+1, CHAR(type2str(t_ai)));
33432 ripley 116
	lens[i] = length(a[i]);
48156 ripley 117
	if(lens[i] == 0) return allocVector(STRSXP, 0);
33194 ripley 118
    }
48156 ripley 119
 
48378 maechler 120
#define CHECK_maxlen							\
121
    maxlen = nfmt;							\
122
    for(i = 0; i < nargs; i++)						\
123
	if(maxlen < lens[i]) maxlen = lens[i];				\
124
    if(maxlen % nfmt)							\
125
	error(_("arguments cannot be recycled to the same length"));	\
126
    for(i = 0; i < nargs; i++)						\
127
	if(maxlen % lens[i])						\
128
	    error(_("arguments cannot be recycled to the same length"))
129
 
130
    CHECK_maxlen;
131
 
48156 ripley 132
    outputString = R_AllocStringBuffer(0, &outbuff);
133
 
44711 ripley 134
    /* We do the format analysis a row at a time */
33194 ripley 135
    for(ns = 0; ns < maxlen; ns++) {
136
	outputString[0] = '\0';
45847 ripley 137
	use_UTF8 = getCharCE(STRING_ELT(format, ns % nfmt)) == CE_UTF8;
138
	if (!use_UTF8) {
139
	    for(i = 0; i < nargs; i++) {
140
		if (!isString(a[i])) continue;
141
		if (getCharCE(STRING_ELT(a[i], ns % lens[i])) == CE_UTF8) {
142
		    use_UTF8 = TRUE; break;
143
		}
144
	    }
145
	}
48376 maechler 146
 
147
	formatString = TRANSLATE_CHAR(format, ns % nfmt);
33194 ripley 148
	n = strlen(formatString);
149
	if (n > MAXLINE)
44700 ripley 150
	    error(_("'fmt' length exceeds maximal format length %d"), MAXLINE);
45446 ripley 151
	/* process the format string */
44700 ripley 152
	for (cur = 0, cnt = 0; cur < n; cur += chunk) {
59226 ripley 153
	    const char *curFormat = formatString + cur, *ss;
154
	    char *starc;
41978 ripley 155
	    ss = NULL;
33194 ripley 156
	    if (formatString[cur] == '%') { /* handle special format command */
19133 maechler 157
 
33194 ripley 158
		if (cur < n - 1 && formatString[cur + 1] == '%') {
159
		    /* take care of %% in the format */
160
		    chunk = 2;
161
		    strcpy(bit, "%");
162
		}
163
		else {
164
		    /* recognise selected types from Table B-1 of K&R */
48376 maechler 165
		    /* NB: we deal with "%%" in branch above. */
33203 ripley 166
		    /* This is MBCS-OK, as we are in a format spec */
49773 ripley 167
		    chunk = strcspn(curFormat + 1, "diosfeEgGxXaA") + 2;
33194 ripley 168
		    if (cur + chunk > n)
48376 maechler 169
			error(_("unrecognised format specification '%s'"), curFormat);
33194 ripley 170
 
48376 maechler 171
		    strncpy(fmt, curFormat, chunk);
33194 ripley 172
		    fmt[chunk] = '\0';
173
 
174
		    nthis = -1;
175
		    /* now look for %n$ or %nn$ form */
176
		    if (strlen(fmt) > 3 && fmt[1] >= '1' && fmt[1] <= '9') {
177
			v = fmt[1] - '0';
178
			if(fmt[2] == '$') {
179
			    if(v > nargs)
41686 ripley 180
				error(_("reference to non-existent argument %d"), v);
33194 ripley 181
			    nthis = v-1;
182
			    memmove(fmt+1, fmt+3, strlen(fmt)-2);
59722 murdoch 183
			} else if(fmt[2] >= '0' && fmt[2] <= '9' && fmt[3] == '$') {
33194 ripley 184
			    v = 10*v + fmt[2] - '0';
185
			    if(v > nargs)
41686 ripley 186
				error(_("reference to non-existent argument %d"), v);
33194 ripley 187
			    nthis = v-1;
188
			    memmove(fmt+1, fmt+4, strlen(fmt)-3);
189
			}
33117 ripley 190
		    }
35625 urbaneks 191
 
42045 ripley 192
		    starc = Rf_strchr(fmt, '*');
48376 maechler 193
		    if (starc) { /* handle  *  format if present */
36948 ripley 194
			nstar = -1;
35625 urbaneks 195
			if (strlen(starc) > 3 && starc[1] >= '1' && starc[1] <= '9') {
196
			    v = starc[1] - '0';
197
			    if(starc[2] == '$') {
198
				if(v > nargs)
41686 ripley 199
				    error(_("reference to non-existent argument %d"), v);
35625 urbaneks 200
				nstar = v-1;
201
				memmove(starc+1, starc+3, strlen(starc)-2);
59722 murdoch 202
			    } else if(starc[2] >= '0' && starc[2] <= '9'
35625 urbaneks 203
				      && starc[3] == '$') {
204
				v = 10*v + starc[2] - '0';
205
				if(v > nargs)
41686 ripley 206
				    error(_("reference to non-existent argument %d"), v);
35625 urbaneks 207
				nstar = v-1;
208
				memmove(starc+1, starc+4, strlen(starc)-3);
209
			    }
210
			}
211
 
212
			if(nstar < 0) {
41686 ripley 213
			    if (cnt >= nargs) error(_("too few arguments"));
35625 urbaneks 214
			    nstar = cnt++;
215
			}
216
 
42045 ripley 217
			if (Rf_strchr(starc+1, '*'))
44711 ripley 218
			    error(_("at most one asterisk '*' is supported in each conversion specification"));
35625 urbaneks 219
 
39866 duncan 220
			_this = a[nstar];
44711 ripley 221
			if(ns == 0 && TYPEOF(_this) == REALSXP) {
39866 duncan 222
			    _this = coerceVector(_this, INTSXP);
44711 ripley 223
			    PROTECT(a[nstar] = _this);
224
			    nprotect++;
225
			}
39866 duncan 226
			if(TYPEOF(_this) != INTSXP || LENGTH(_this)<1 ||
227
			   INTEGER(_this)[ns % LENGTH(_this)] == NA_INTEGER)
44711 ripley 228
			    error(_("argument for '*' conversion specification must be a number"));
39866 duncan 229
			star_arg = INTEGER(_this)[ns % LENGTH(_this)];
48376 maechler 230
			has_star = TRUE;
33194 ripley 231
		    }
48376 maechler 232
		    else
233
			has_star = FALSE;
19133 maechler 234
 
35625 urbaneks 235
		    if (fmt[strlen(fmt) - 1] == '%') {
236
			/* handle % with formatting options */
237
			if (has_star)
62580 ripley 238
			    snprintf(bit, MAXLINE+1, fmt, star_arg);
35625 urbaneks 239
			else
48399 maechler 240
			    strcpy(bit, fmt);
241
			/* was sprintf(..)  for which some compiler warn */
35625 urbaneks 242
		    } else {
48415 maechler 243
			Rboolean did_this = FALSE;
35625 urbaneks 244
			if(nthis < 0) {
41686 ripley 245
			    if (cnt >= nargs) error(_("too few arguments"));
35625 urbaneks 246
			    nthis = cnt++;
33194 ripley 247
			}
39866 duncan 248
			_this = a[nthis];
36948 ripley 249
			if (has_star) {
55743 luke 250
			    size_t nf; char *p, *q = fmt2;
36948 ripley 251
			    for (p = fmt; *p; p++)
252
				if (*p == '*') q += sprintf(q, "%d", star_arg);
253
				else *q++ = *p;
254
			    *q = '\0';
48399 maechler 255
			    nf = strlen(fmt2);
256
			    if (nf > MAXLINE)
59181 murdoch 257
				error(_("'fmt' length exceeds maximal format length %d"),
48399 maechler 258
				      MAXLINE);
36948 ripley 259
			    fmtp = fmt2;
260
			} else fmtp = fmt;
45446 ripley 261
 
48415 maechler 262
#define CHECK_this_length						\
263
			PROTECT(_this);					\
264
			thislen = length(_this);			\
265
			if(thislen == 0)				\
266
			    error(_("coercion has changed vector length to 0"))
267
 
48376 maechler 268
			/* Now let us see if some minimal coercion
269
			   would be sensible, but only do so once, for ns = 0: */
44711 ripley 270
			if(ns == 0) {
48415 maechler 271
			    SEXP tmp; Rboolean do_check;
46947 ripley 272
			    switch(*findspec(fmtp)) {
44700 ripley 273
			    case 'd':
274
			    case 'i':
49773 ripley 275
			    case 'o':
44700 ripley 276
			    case 'x':
277
			    case 'X':
278
				if(TYPEOF(_this) == REALSXP) {
279
				    double r = REAL(_this)[0];
280
				    if((double)((int) r) == r)
281
					_this = coerceVector(_this, INTSXP);
44711 ripley 282
				    PROTECT(a[nthis] = _this);
283
				    nprotect++;
44700 ripley 284
				}
285
				break;
46517 ripley 286
			    case 'a':
287
			    case 'A':
44700 ripley 288
			    case 'e':
289
			    case 'f':
290
			    case 'g':
291
			    case 'E':
292
			    case 'G':
48415 maechler 293
				if(TYPEOF(_this) != REALSXP &&
294
				   /* no automatic as.double(<string>) : */
295
				   TYPEOF(_this) != STRSXP) {
44700 ripley 296
				    PROTECT(tmp = lang2(install("as.double"), _this));
48378 maechler 297
#define COERCE_THIS_TO_A						\
298
				    _this = eval(tmp, env);		\
299
				    UNPROTECT(1);			\
300
				    PROTECT(a[nthis] = _this);		\
301
				    nprotect++;				\
48415 maechler 302
				    did_this = TRUE;			\
303
				    CHECK_this_length;			\
48378 maechler 304
				    do_check = (lens[nthis] == maxlen);	\
48415 maechler 305
				    lens[nthis] = thislen; /* may have changed! */ \
306
				    if(do_check && thislen < maxlen) {	\
48378 maechler 307
					CHECK_maxlen;			\
308
				    }
309
 
310
				    COERCE_THIS_TO_A
44700 ripley 311
				}
312
				break;
313
			    case 's':
314
				if(TYPEOF(_this) != STRSXP) {
50894 ripley 315
				    /* as.character method might call sprintf() */
55743 luke 316
				    size_t nc = strlen(outputString);
50894 ripley 317
				    char *z = Calloc(nc+1, char);
318
				    strcpy(z, outputString);
44711 ripley 319
				    PROTECT(tmp = lang2(install("as.character"), _this));
48378 maechler 320
 
321
				    COERCE_THIS_TO_A
50894 ripley 322
				    strcpy(outputString, z);
323
				    Free(z);
44700 ripley 324
				}
325
				break;
326
			    default:
327
				break;
35625 urbaneks 328
			    }
48415 maechler 329
			} /* ns == 0 (first-time only) */
45446 ripley 330
 
48415 maechler 331
			if(!did_this)
332
			    CHECK_this_length;
45446 ripley 333
 
39866 duncan 334
			switch(TYPEOF(_this)) {
35625 urbaneks 335
			case LGLSXP:
336
			    {
39866 duncan 337
				int x = LOGICAL(_this)[ns % thislen];
46947 ripley 338
				if (checkfmt(fmtp, "di"))
48399 maechler 339
				    error(_("invalid format '%s'; %s"), fmtp,
35625 urbaneks 340
					  _("use format %d or %i for logical objects"));
341
				if (x == NA_LOGICAL) {
36948 ripley 342
				    fmtp[strlen(fmtp)-1] = 's';
48399 maechler 343
				    _my_sprintf("NA")
35625 urbaneks 344
				} else {
48399 maechler 345
				    _my_sprintf(x)
35625 urbaneks 346
				}
347
				break;
348
			    }
349
			case INTSXP:
350
			    {
39866 duncan 351
				int x = INTEGER(_this)[ns % thislen];
49773 ripley 352
				if (checkfmt(fmtp, "dioxX"))
48399 maechler 353
				    error(_("invalid format '%s'; %s"), fmtp,
49773 ripley 354
					  _("use format %d, %i, %o, %x or %X for integer objects"));
35625 urbaneks 355
				if (x == NA_INTEGER) {
36948 ripley 356
				    fmtp[strlen(fmtp)-1] = 's';
48399 maechler 357
				    _my_sprintf("NA")
35625 urbaneks 358
				} else {
48399 maechler 359
				    _my_sprintf(x)
35625 urbaneks 360
				}
361
				break;
362
			    }
363
			case REALSXP:
364
			    {
39866 duncan 365
				double x = REAL(_this)[ns % thislen];
46947 ripley 366
				if (checkfmt(fmtp, "aAfeEgG"))
48399 maechler 367
				    error(_("invalid format '%s'; %s"), fmtp,
46517 ripley 368
					  _("use format %f, %e, %g or %a for numeric objects"));
35625 urbaneks 369
				if (R_FINITE(x)) {
48399 maechler 370
				    _my_sprintf(x)
35625 urbaneks 371
				} else {
42045 ripley 372
				    char *p = Rf_strchr(fmtp, '.');
35625 urbaneks 373
				    if (p) {
374
					*p++ = 's'; *p ='\0';
375
				    } else
36948 ripley 376
					fmtp[strlen(fmtp)-1] = 's';
35625 urbaneks 377
				    if (ISNA(x)) {
36948 ripley 378
					if (strcspn(fmtp, " ") < strlen(fmtp))
48399 maechler 379
					    _my_sprintf(" NA")
36948 ripley 380
					else
48399 maechler 381
					    _my_sprintf("NA")
35625 urbaneks 382
				    } else if (ISNAN(x)) {
36948 ripley 383
					if (strcspn(fmtp, " ") < strlen(fmtp))
48399 maechler 384
					    _my_sprintf(" NaN")
36948 ripley 385
					else
48399 maechler 386
					    _my_sprintf("NaN")
35625 urbaneks 387
				    } else if (x == R_PosInf) {
36948 ripley 388
					if (strcspn(fmtp, "+") < strlen(fmtp))
48399 maechler 389
					    _my_sprintf("+Inf")
36948 ripley 390
					else if (strcspn(fmtp, " ") < strlen(fmtp))
48399 maechler 391
					    _my_sprintf(" Inf")
35625 urbaneks 392
					else
48399 maechler 393
					    _my_sprintf("Inf")
36948 ripley 394
				    } else if (x == R_NegInf)
48399 maechler 395
					_my_sprintf("-Inf")
35625 urbaneks 396
				}
397
				break;
398
			    }
399
			case STRSXP:
44711 ripley 400
			    /* NA_STRING will be printed as 'NA' */
46947 ripley 401
			    if (checkfmt(fmtp, "s"))
48399 maechler 402
				error(_("invalid format '%s'; %s"), fmtp,
403
				      _("use format %s for character objects"));
48376 maechler 404
 
405
			    ss = TRANSLATE_CHAR(_this, ns % thislen);
42001 ripley 406
			    if(fmtp[1] != 's') {
407
				if(strlen(ss) > MAXLINE)
44711 ripley 408
				    warning(_("likely truncation of character string to %d characters"),
409
					    MAXLINE-1);
48399 maechler 410
				_my_sprintf(ss)
42001 ripley 411
				bit[MAXLINE] = '\0';
412
				ss = NULL;
413
			    }
35625 urbaneks 414
			    break;
45446 ripley 415
 
35625 urbaneks 416
			default:
41686 ripley 417
			    error(_("unsupported type"));
35625 urbaneks 418
			    break;
33436 ripley 419
			}
33143 ripley 420
 
35625 urbaneks 421
			UNPROTECT(1);
33194 ripley 422
		    }
19717 ripley 423
		}
19133 maechler 424
	    }
33194 ripley 425
	    else { /* not '%' : handle string part */
48376 maechler 426
		char *ch = Rf_strchr(curFormat, '%'); /* MBCS-aware version used */
55743 luke 427
		chunk = (ch) ? (size_t) (ch - curFormat) : strlen(curFormat);
48376 maechler 428
		strncpy(bit, curFormat, chunk);
33194 ripley 429
		bit[chunk] = '\0';
430
	    }
41978 ripley 431
	    if(ss) {
45446 ripley 432
		outputString = R_AllocStringBuffer(strlen(outputString) +
41978 ripley 433
						   strlen(ss) + 1, &outbuff);
434
		strcat(outputString, ss);
435
	    } else {
45446 ripley 436
		outputString = R_AllocStringBuffer(strlen(outputString) +
41978 ripley 437
						   strlen(bit) + 1, &outbuff);
438
		strcat(outputString, bit);
439
	    }
48376 maechler 440
	}  /* end for ( each chunk ) */
441
 
48378 maechler 442
	if(ns == 0) { /* may have adjusted maxlen now ... */
443
	    PROTECT(ans = allocVector(STRSXP, maxlen));
444
	    nprotect++;
445
	}
48376 maechler 446
	SET_STRING_ELT(ans, ns, mkCharCE(outputString,
45847 ripley 447
					 use_UTF8 ? CE_UTF8 : CE_NATIVE));
48376 maechler 448
    } /* end for(ns ...) */
19133 maechler 449
 
44700 ripley 450
    UNPROTECT(nprotect);
41978 ripley 451
    R_FreeStringBufferL(&outbuff);
19133 maechler 452
    return ans;
453
}