The R Project SVN R

Rev

Rev 68948 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 *  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;
}