blob: 1791c8d96a92969c6dda46460639165c8552de20 [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 2002--2013 The R Core Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, a copy is available at
* https://www.R-project.org/Licenses/
*
* Formerly part of src/main/sprintf.c
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <Defn.h>
#include "RBufferUtils.h"
#undef _
#ifdef ENABLE_NLS
#include <libintl.h>
#define _(String) dgettext ("tools", String)
#else
#define _(String) (String)
#endif
#define MAXLINE MAXELTSIZE
#define MAXNARGS 100
/* ^^^ not entirely arbitrary, but strongly linked to allowing %$1 to %$99 !*/
#define TRANSLATE_CHAR(_STR_, _i_) \
((use_UTF8) ? translateCharUTF8(STRING_ELT(_STR_, _i_)) \
: translateChar(STRING_ELT(_STR_, _i_)))
SEXP getfmts(SEXP format)
{
int cnt, v, nfmt;
char fmt[MAXLINE+1], bit[MAXLINE+1];
const char *formatString;
size_t n, cur, chunk, maxlen = 0;
int nthis, nstar;
Rboolean use_UTF8;
const void *vmax = vmaxget();
SEXP res = PROTECT(allocVector(STRSXP, MAXNARGS));
#define SET_RESULT(n, s) { \
if (n >= MAXNARGS) error(_("only %d arguments are allowed"), MAXNARGS); \
maxlen = (n) < maxlen ? maxlen : (n) + 1; \
SET_STRING_ELT(res, (n), mkChar(s)); \
}
if (!isString(format)) error(_("'fmt' is not a character vector"));
nfmt = LENGTH(format);
if (nfmt != 1)
error(_("'fmt' must be length 1"));
use_UTF8 = getCharCE(STRING_ELT(format, 0)) == CE_UTF8;
formatString = TRANSLATE_CHAR(format, 0);
n = strlen(formatString);
if (n > MAXLINE)
error(_("'fmt' length exceeds maximal format length %d"), MAXLINE);
/* process the format string */
for (cur = 0, cnt = 0; cur < n; cur += chunk) {
const char *curFormat = formatString + cur;
char *starc;
if (formatString[cur] == '%') { /* handle special format command */
if (cur < n - 1 && formatString[cur + 1] == '%') {
/* take care of %% in the format */
chunk = 2;
strcpy(bit, "%");
}
else {
/* recognise selected types from Table B-1 of K&R */
/* NB: we deal with "%%" in branch above. */
/* This is MBCS-OK, as we are in a format spec */
/* Include formats c, u, p and n as well as the R formats; this needs to match */
/* C code as well */
chunk = strcspn(curFormat + 1, "diosfeEgGxXaAcupn") + 2;
if (cur + chunk > n)
error(_("unrecognised format specification '%s'"), curFormat);
strncpy(fmt, curFormat, chunk);
fmt[chunk] = '\0';
nthis = -1;
/* now look for %n$ or %nn$ form */
if (strlen(fmt) > 3 && fmt[1] >= '1' && fmt[1] <= '9') {
v = fmt[1] - '0';
if(fmt[2] == '$') {
nthis = v-1;
memmove(fmt+1, fmt+3, strlen(fmt)-2);
} else if(fmt[2] >= '0' && fmt[2] <= '9' && fmt[3] == '$') {
v = 10*v + fmt[2] - '0';
nthis = v-1;
memmove(fmt+1, fmt+4, strlen(fmt)-3);
}
}
starc = Rf_strchr(fmt, '*');
if (starc) { /* handle * format if present */
nstar = -1;
if (strlen(starc) > 3 && starc[1] >= '1' && starc[1] <= '9') {
v = starc[1] - '0';
if(starc[2] == '$') {
nstar = v-1;
memmove(starc+1, starc+3, strlen(starc)-2);
} else if(starc[2] >= '0' && starc[2] <= '9'
&& starc[3] == '$') {
v = 10*v + starc[2] - '0';
nstar = v-1;
memmove(starc+1, starc+4, strlen(starc)-3);
}
}
if(nstar < 0) {
nstar = cnt++;
}
if (Rf_strchr(starc+1, '*'))
error(_("at most one asterisk '*' is supported in each conversion specification"));
SET_RESULT(nstar, "*");
}
if (fmt[strlen(fmt) - 1] == '%') {
} else {
if(nthis < 0) {
nthis = cnt++;
}
SET_RESULT(nthis, fmt);
}
}
}
else { /* not '%' : handle string part */
char *ch = Rf_strchr(curFormat, '%'); /* MBCS-aware version used */
chunk = (ch) ? (size_t) (ch - curFormat) : strlen(curFormat);
strncpy(bit, curFormat, chunk);
bit[chunk] = '\0';
}
} /* end for ( each chunk ) */
res = xlengthgets(res, maxlen);
vmaxset(vmax);
UNPROTECT(1);
return res;
}