The R Project SVN R

Rev

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

/*
 *  R : A Computer Language for Statistical Data Analysis
 *  Copyright (C) 1995-1996   Robert Gentleman and Ross Ihaka
 *  Copyright (C) 1997-2007   Robert Gentleman, Ross Ihaka
 *                            and the R Development 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
 *  http://www.r-project.org/Licenses/
 */

/* <UTF8> char here is either ASCII or handled as a whole */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h> /* for putenv */
#include <Defn.h>
#include <R_ext/Riconv.h>
#include <Rinterface.h>
#include <errno.h>

/*
  See ../unix/system.txt for a description of some of these functions.
  Formally part of ../unix/sys-common.c.
 */

/* The __APPLE__ and __APPLE_CC__ defines are for OS X */

/*
 * FILESYSTEM INTERACTION
 */

/*
 * This call provides a simple interface to the "stat" system call.
 */

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif

#if HAVE_AQUA
extern int (*ptr_CocoaSystem)(char*);
extern  Rboolean useaqua;
#endif

Rboolean attribute_hidden R_FileExists(const char *path)
{
    struct stat sb;
    return stat(R_ExpandFileName(path), &sb) == 0;
}

double attribute_hidden R_FileMtime(const char *path)
{
    struct stat sb;
    if (stat(R_ExpandFileName(path), &sb) != 0)
    error(_("cannot determine file modification time of '%s'"), path);
    return sb.st_mtime;
}

    /*
     *  Unix file names which begin with "." are invisible.
     */

Rboolean attribute_hidden R_HiddenFile(const char *name)
{
    if (name && name[0] != '.') return 0;
    else return 1;
}

/* The MSVC runtime has a global to determine whether an unspecified file open
   is in text or binary mode.  We force explicit text mode here to avoid depending 
   on that global, which may have been changed by user code.
*/

#ifdef Win32
static char fixedmode[6]; /* Rconnection can have a mode of 4 chars plus a null; we might add one char */

static char * fixmode(const char *mode)
{
    fixedmode[4] = '\0';
    strncpy(fixedmode, mode, 4);
    if (!strpbrk(fixedmode, "bt")) {
        strcat(fixedmode, "t");
    }
    return fixedmode;
}

#else
#define fixmode(mode) (mode)
#endif

FILE *R_fopen(const char *filename, const char *mode)
{
    return(filename ? fopen(filename, fixmode(mode)) : NULL );
}

/* The point of this function is to allow file names in foreign
   character sets.  On Unix-alikes in a UTF-8 locale all that is
   needed is to convert file names to UTF-8, since they will be stored
   in UTF-8.  For other locales, it seems that there is no way to specify
   a file name in UTF-8.

   On NT-based versions of Windows, file names are stored in 'Unicode'
   (UCS-2), and _wfopen is provided to access them by UCS-2 names.
   This requires NT, so is currently disabled.
*/

#if 0 && defined(Win32)
FILE *RC_fopen(const SEXP fn, const char *mode, const Rboolean expand)
{
    wchar_t filename[MAX_PATH+1], wmode[10];
    void *obj;
    char *from = "", *inbuf, *outbuf;
    size_t inb, outb, res;
    
    if(IS_LATIN1(fn)) from = "latin1";
    if(IS_UTF8(fn)) from = "UTF=8";
    obj = Riconv_open("UCS-2LE", from);
    if(obj == (void *)(-1)) error(_("unsupported conversion"));

    if(expand) inbuf = R_ExpandFileName(CHAR(fn)); else inbuf = CHAR(fn);

    inb = strlen(inbuf)+1; outb = 2*(MAX_PATH+1);
    outbuf = (char *) filename;
    res = Riconv(obj, &inbuf , &inb, &outbuf, &outb);
    Riconv_close(obj);
    if(res == -1 || inb > 0) error(_("file name conversion problem"));

    mbstowcs(wmode, fixmode(mode), 10);
    return _wfopen(filename, wmode);
}

#else
FILE *RC_fopen(const SEXP fn, const char *mode, const Rboolean expand)
{
    const char *filename = translateChar(fn);
    if(!filename) return NULL;
    if(expand) return fopen(R_ExpandFileName(filename), mode);
    else return fopen(filename, mode);
}
#endif

/*
 *  SYSTEM INFORMATION
 */

          /* The location of the R system files */

char *R_HomeDir()
{
    return getenv("R_HOME");
}


SEXP attribute_hidden do_interactive(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    return ScalarLogical( (R_Interactive) ? 1 : 0 );
}

SEXP attribute_hidden do_tempdir(SEXP call, SEXP op, SEXP args, SEXP env)
{
    checkArity(op, args);
    return mkString(R_TempDir);
}


SEXP attribute_hidden do_tempfile(SEXP call, SEXP op, SEXP args, SEXP env)
{
    SEXP  ans, pattern, tempdir;
    const char *tn, *td;
    char *tm;
    int i, n1, n2, slen;

    checkArity(op, args);
    pattern = CAR(args); n1 = length(pattern);
    tempdir = CADR(args); n2 = length(tempdir);
    if (!isString(pattern))
        error(_("invalid filename pattern"));
    if (!isString(tempdir))
        error(_("invalid '%s' value"), "tempdir");
    if (n1 < 1)
    error(_("no 'pattern'"));
    if (n2 < 1)
    error(_("no 'tempdir'"));
    slen = (n1 > n2) ? n1 : n2;
    PROTECT(ans = allocVector(STRSXP, slen));
    for(i = 0; i < slen; i++) {
    tn = translateChar( STRING_ELT( pattern , i%n1 ) );
    td = translateChar( STRING_ELT( tempdir , i%n2 ) );
    /* try to get a new file name */
    tm = R_tmpnam(tn, td);
    SET_STRING_ELT(ans, i, mkChar(tm));
    if(tm) free(tm);
    }
    UNPROTECT(1);
    return (ans);
}

#ifdef HAVE_POPEN
FILE *R_popen(const char *command, const char *type)
{
    FILE *fp;
#ifdef __APPLE_CC__
    /* Luke recommends this to fix PR#1140 */
    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGPROF);
    sigprocmask(SIG_BLOCK, &ss,  NULL);
    fp = popen(command, type);
    sigprocmask(SIG_UNBLOCK, &ss, NULL);
#else
    fp = popen(command, type);
#endif
    return fp;
}
#endif /* HAVE_POPEN */

int R_system(const char *command)
{
    int val;
#ifdef __APPLE_CC__
    /* Luke recommends this to fix PR#1140 */
    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGPROF);
    sigprocmask(SIG_BLOCK, &ss,  NULL);
#ifdef HAVE_AQUA
    char *cmdcpy;
    if(useaqua) {
        /* FIXME, is Cocoa's interface not const char*? */
        cmdcpy = acopy_string(command);
    val = ptr_CocoaSystem(cmdcpy);
    }
    else
#endif
    val = system(command);
    sigprocmask(SIG_UNBLOCK, &ss, NULL);
#else
    val = system(command);
#endif
    return val;
}

#if defined(__APPLE__)
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
#else
extern char ** environ;
#endif

SEXP attribute_hidden do_getenv(SEXP call, SEXP op, SEXP args, SEXP env)
{
    int i, j;
    char *s;
    SEXP ans;

    checkArity(op, args);

    if (!isString(CAR(args)))
    error(_("wrong type for argument"));

    if (!isString(CADR(args)) || LENGTH(CADR(args)) != 1)
    error(_("wrong type for argument"));

    i = LENGTH(CAR(args));
    if (i == 0) {
    char **e;
    for (i = 0, e = environ; *e != NULL; i++, e++);
    PROTECT(ans = allocVector(STRSXP, i));
    for (i = 0, e = environ; *e != NULL; i++, e++)
        SET_STRING_ELT(ans, i, mkChar(*e));
    } else {
    PROTECT(ans = allocVector(STRSXP, i));
    for (j = 0; j < i; j++) {
        s = getenv(translateChar(STRING_ELT(CAR(args), j)));
        if (s == NULL)
        SET_STRING_ELT(ans, j, STRING_ELT(CADR(args), 0));
        else {
        SEXP tmp;
        if(known_to_be_latin1) tmp = mkCharEnc(s, LATIN1_MASK);
        else if(known_to_be_utf8) tmp = mkCharEnc(s, UTF8_MASK);
        else tmp = mkChar(s);
        SET_STRING_ELT(ans, j, tmp);
        }
    }
    }
    UNPROTECT(1);
    return (ans);
}

#if !defined(HAVE_SETENV) && defined(HAVE_PUTENV)
static int Rputenv(const char *nm, const char *val)
{
    char *buf;
    buf = (char *) malloc((strlen(nm) + strlen(val) + 2) * sizeof(char));
    if(!buf) return 1;
    sprintf(buf, "%s=%s", nm, val);
    if(putenv(buf)) return 1;
    /* no free here: storage remains in use */
    return 0;
}
#endif


SEXP attribute_hidden do_setenv(SEXP call, SEXP op, SEXP args, SEXP env)
{
#if defined(HAVE_PUTENV) || defined(HAVE_SETENV)
    int i, n;
    SEXP ans, nm, vars;

    checkArity(op, args);

    if (!isString(nm = CAR(args)))
    error(_("wrong type for argument"));
    if (!isString(vars = CADR(args)))
    error(_("wrong type for argument"));
    if(LENGTH(nm) != LENGTH(vars))
    error(_("wrong length for argument"));

    n = LENGTH(vars);
    PROTECT(ans = allocVector(LGLSXP, n));
#ifdef HAVE_SETENV
    for (i = 0; i < n; i++)
    LOGICAL(ans)[i] = setenv(translateChar(STRING_ELT(nm, i)),
                 translateChar(STRING_ELT(vars, i)),
                 1) == 0;
#else
    for (i = 0; i < n; i++)
    LOGICAL(ans)[i] = Rputenv(translateChar(STRING_ELT(nm, i)),
                  translateChar(STRING_ELT(vars, i))) == 0;
#endif
    UNPROTECT(1);
    return ans;
#else
    error(_("'Sys.setenv' is not available on this system"));
    return R_NilValue; /* -Wall */
#endif
}

SEXP attribute_hidden do_unsetenv(SEXP call, SEXP op, SEXP args, SEXP env)
{
    int i, n;
    SEXP ans, vars;

    checkArity(op, args);

    if (!isString(vars = CAR(args)))
        error(_("wrong type for argument"));
    n = LENGTH(vars);

#if defined(HAVE_UNSETENV) || defined(HAVE_PUTENV_UNSET) || defined(HAVE_PUTENV_UNSET2)
#ifdef HAVE_UNSETENV
    for (i = 0; i < n; i++) unsetenv(translateChar(STRING_ELT(vars, i)));
#elif defined(HAVE_PUTENV_UNSET)
    for (i = 0; i < n; i++) {
    char buf[1000];
    snprintf(buf, 1000, "%s",  translateChar(STRING_ELT(vars, i)));
    putenv(buf);
    }
#elif defined(HAVE_PUTENV_UNSET2)
    for (i = 0; i < n; i++) {
    char buf[1000];
    snprintf(buf, 1000, "%s=", translateChar(STRING_ELT(vars, i)));
    putenv(buf);
    }
#endif

#elif defined(HAVE_PUTENV) || defined(HAVE_SETENV)
    warning(_("this system cannot unset environment variables: setting to \"\""));
    n = LENGTH(vars);
    for (i = 0; i < n; i++) {
#ifdef HAVE_SETENV
    setenv(translateChar(STRING_ELT(vars, i)), "", 1);
#else
    Rputenv(translateChar(STRING_ELT(vars, i)), "");
#endif
    }

#else
    warning(_("'Sys.unsetenv' is not available on this system"));
#endif

    PROTECT(ans = allocVector(LGLSXP, n));
    for (i = 0; i < n; i++)
        LOGICAL(ans)[i] = !getenv(translateChar(STRING_ELT(vars, i)));
    UNPROTECT(1);
    return ans;
}

#if defined(HAVE_ICONV_H) && defined(ICONV_LATIN1)
# include <iconv.h>
#endif

#ifdef HAVE_ICONVLIST
static unsigned int cnt;

static int
count_one (unsigned int namescount, const char * const *names, void *data)
{
    cnt += namescount;
    return 0;
}

static int
write_one (unsigned int namescount, const char * const *names, void *data)
{
  unsigned int i;
  SEXP ans = (SEXP) data;

  for (i = 0; i < namescount; i++)
      SET_STRING_ELT(ans, cnt++, mkChar(names[i]));
  return 0;
}
#endif

#include "RBufferUtils.h"

/* iconv(x, from, to, sub) */
SEXP attribute_hidden do_iconv(SEXP call, SEXP op, SEXP args, SEXP env)
{
#if defined(HAVE_ICONV) && defined(ICONV_LATIN1)
    SEXP ans, x = CAR(args), si;
    void * obj;
    int i, j, nout;
    const char *inbuf;
    char *outbuf;
    const char *sub;
    size_t inb, outb, res;
    R_StringBuffer cbuff = {NULL, 0, MAXELTSIZE};

    checkArity(op, args);
    if(isNull(x)) {  /* list locales */
#ifdef HAVE_ICONVLIST
    cnt = 0;
    iconvlist(count_one, NULL);
    PROTECT(ans = allocVector(STRSXP, cnt));
    cnt = 0;
    iconvlist(write_one, (void *)ans);
#else
    PROTECT(ans = R_NilValue);
#endif
    } else {
    const char *from, *to;
    Rboolean isLatin1 = FALSE, isUTF8 = FALSE;

    if(TYPEOF(x) != STRSXP)
        error(_("'x' must be a character vector"));
    if(!isString(CADR(args)) || length(CADR(args)) != 1)
        error(_("invalid '%s' argument"), "from");
    if(!isString(CADDR(args)) || length(CADDR(args)) != 1)
        error(_("invalid '%s' argument"), "to");
    if(!isString(CADDDR(args)) || length(CADDDR(args)) != 1)
        error(_("invalid '%s' argument"), "sub");
    if(STRING_ELT(CADDDR(args), 0) == NA_STRING) sub = NULL;
    else sub = translateChar(STRING_ELT(CADDDR(args), 0));
    from = CHAR(STRING_ELT(CADR(args), 0)); /* ASCII */
    to = CHAR(STRING_ELT(CADDR(args), 0));
    /* Should we do something about marked CHARSXPs in 'from = ""'? */
    if(streql(to, "UTF-8")) isUTF8 = TRUE;
    if(streql(to, "latin1") || streql(to, "ISO_8859-1")) isLatin1 = TRUE;
    if(streql(to, "") && known_to_be_latin1) isLatin1 = TRUE;
    if(streql(to, "") && known_to_be_utf8) isUTF8 = TRUE;
    obj = Riconv_open(to, from);
    if(obj == (iconv_t)(-1))
        error(_("unsupported conversion"));
    PROTECT(ans = duplicate(x));
    R_AllocStringBuffer(0, &cbuff);  /* 0 -> default */
    for(i = 0; i < LENGTH(x); i++) {
        si = STRING_ELT(x, i);
    top_of_loop:
        inbuf = CHAR(si); inb = LENGTH(si);
        outbuf = cbuff.data; outb = cbuff.bufsize - 1;
        /* First initialize output */
        Riconv (obj, NULL, NULL, &outbuf, &outb);
        next_char:
        /* Then convert input  */
        res = Riconv(obj, &inbuf , &inb, &outbuf, &outb);
        *outbuf = '\0';
        /* other possible error conditions are incomplete
           and invalid multibyte chars */
        if(res == -1 && errno == E2BIG) {
        R_AllocStringBuffer(2*cbuff.bufsize, &cbuff);
        goto top_of_loop;
        } else if(res == -1 && errno == EILSEQ && sub) {
        /* it seems this gets thrown for non-convertible input too */
        if(strcmp(sub, "byte") == 0) {
            if(outb < 5) {
            R_AllocStringBuffer(2*cbuff.bufsize, &cbuff);
            goto top_of_loop;
            }
            snprintf(outbuf, 5, "<%02x>", (unsigned char)*inbuf);
            outbuf += 4; outb -= 4;
        } else {
            if(outb < strlen(sub)) {
            R_AllocStringBuffer(2*cbuff.bufsize, &cbuff);
            goto top_of_loop;
            }
            memcpy(outbuf, sub, j = strlen(sub)); 
            outbuf += j;
            outb -= j;
        }
        inbuf++; inb--;
        goto next_char;
        }

        if(res != -1 && inb == 0) {
        /* we can only put the result in the CHARSXP
           cache if it does not contain nuls. */
        Rboolean has_nul = FALSE;
        char *p = cbuff.data;

        nout = cbuff.bufsize - 1 - outb;
        for(j = 0; j < nout; j++) if(!*p++) {has_nul = TRUE; break;}
        if(has_nul) {
            si = allocString(nout);
            memcpy(CHAR_RW(si), cbuff.data, nout);
            if(isLatin1) SET_LATIN1(si);
            if(isUTF8) SET_UTF8(si);
        } else {
            if(isLatin1) si = mkCharEnc(cbuff.data, LATIN1_MASK);
            else if(isUTF8) si = mkCharEnc(cbuff.data, UTF8_MASK);
            else si = mkChar(cbuff.data);
        }
        SET_STRING_ELT(ans, i, si);
        }
        else SET_STRING_ELT(ans, i, NA_STRING);
    }
    Riconv_close(obj);
    R_FreeStringBuffer(&cbuff);
    }
    UNPROTECT(1);
    return ans;
#else
    error(_("'iconv' is not available on this system"));
    return R_NilValue; /* -Wall */
#endif
}

#if defined(HAVE_ICONV) && defined(ICONV_LATIN1)
void * Riconv_open (const char* tocode, const char* fromcode)
{
#ifdef Win32
    const char *cp = "UTF-8";
#ifndef SUPPORT_UTF8
    cp = locale2charset(NULL);
#endif
    if(strcmp(tocode, "") == 0)  return iconv_open(cp, fromcode);
    else if(strcmp(fromcode, "") == 0) return iconv_open(tocode, cp);
    else return iconv_open(tocode, fromcode);
#else
    /* const char * is right according to POSIX, but libiconv
       plays games so that on Solaris 10 it needs the casts */
    return iconv_open(tocode, fromcode);
#endif
}

/* Should be defined in config.h */
#ifndef ICONV_CONST
# define ICONV_CONST
#endif

size_t Riconv (void *cd, const char **inbuf, size_t *inbytesleft,
           char **outbuf, size_t *outbytesleft)
{
    /* here libiconv has const char **, glibc has char ** for inbuf */
    return iconv((iconv_t) cd, (ICONV_CONST char **) inbuf, inbytesleft, 
         outbuf, outbytesleft);
}

int Riconv_close (void *cd)
{
    return iconv_close((iconv_t) cd);
}

static void *latin1_obj = NULL, *utf8_obj=NULL;

const char *translateChar(SEXP x)
{
    void * obj;
    const char *inbuf, *ans = CHAR(x);
    char *outbuf, *p;
    size_t inb, outb, res;
    R_StringBuffer cbuff = {NULL, 0, MAXELTSIZE};

    if(x == NA_STRING || !(IS_LATIN1(x) || IS_UTF8(x))) return ans;
    if(utf8locale && IS_UTF8(x)) return ans;
    if(latin1locale && IS_LATIN1(x)) return ans;
    if(utf8strIsASCII(CHAR(x))) return ans;

    if(IS_LATIN1(x)) {
    if(!latin1_obj) {
        obj = Riconv_open("", "latin1");
        /* should never happen */
        if(obj == (void *)(-1)) error(_("unsupported conversion"));
        latin1_obj = obj;
    }
    obj = latin1_obj;
    } else {
    if(!utf8_obj) {
        obj = Riconv_open("", "UTF-8");
        /* should never happen */
        if(obj == (void *)(-1)) error(_("unsupported conversion"));
        utf8_obj = obj;
    }
    obj = utf8_obj;
    
    }
    R_AllocStringBuffer(0, &cbuff);
top_of_loop:
    inbuf = ans; inb = strlen(inbuf);
    outbuf = cbuff.data; outb = cbuff.bufsize - 1;
    /* First initialize output */
    Riconv (obj, NULL, NULL, &outbuf, &outb);
next_char:
    /* Then convert input  */
    res = Riconv(obj, &inbuf , &inb, &outbuf, &outb);
    if(res == -1 && errno == E2BIG) {
    R_AllocStringBuffer(2*cbuff.bufsize, &cbuff);
    goto top_of_loop;
    } else if(res == -1 && errno == EILSEQ) {
    if(outb < 5) {
        R_AllocStringBuffer(2*cbuff.bufsize, &cbuff);
        goto top_of_loop;
    }
    snprintf(outbuf, 5, "<%02x>", (unsigned char)*inbuf);
    outbuf += 4; outb -= 4;
    inbuf++; inb--;
    goto next_char;
    }
    *outbuf = '\0';
    res = strlen(cbuff.data) + 1;
    p = R_alloc(res, 1);
    memcpy(p, cbuff.data, res);
    R_FreeStringBuffer(&cbuff);
    return p;
}

void attribute_hidden
invalidate_cached_recodings(void)
{
    latin1_obj = NULL;
    utf8_obj = NULL;
}

#else
void * Riconv_open (const char* tocode, const char* fromcode)
{
    error(_("'iconv' is not available on this system"));
    return (void *)-1;
}

size_t Riconv (void *cd, const char **inbuf, size_t *inbytesleft,
               char **outbuf, size_t *outbytesleft)
{
    error(_("'iconv' is not available on this system"));
    return 0;
}

int Riconv_close (void * cd)
{
    error(_("'iconv' is not available on this system"));
    return -1;
}

const char *translateChar(SEXP x)
{
    return CHAR(x);
}

void attribute_hidden
invalidate_cached_recodings(void)
{
} 
#endif

/* moved from src/unix/sys-unix.c and src/gnuwin32/extra.c */

#ifdef HAVE_STAT
# ifdef HAVE_ACCESS
#  ifdef HAVE_UNISTD_H
#   include <unistd.h>
#  endif
# endif

#ifdef Win32
# define WIN32_LEAN_AND_MEAN 1
# include <windows.h> /* For GetShortPathName */
#endif

#if !defined(S_IFDIR) && defined(__S_IFDIR)
# define S_IFDIR __S_IFDIR
#endif

static int isDir(char *path)
{
    struct stat sb;
    int isdir = 0;
    if(!path) return 0;
    if(stat(path, &sb) == 0) {
    isdir = (sb.st_mode & S_IFDIR) > 0; /* is a directory */
#ifdef HAVE_ACCESS
    /* We want to know if the directory is writable by this user,
       which mode does not tell us */
    isdir &= (access(path, W_OK) == 0);
#endif
    }
    return isdir;
}
#else
static int isDir(char *path)
{
    return 1;
}
#endif /* HAVE_STAT */

#if !HAVE_DECL_MKDTEMP
extern char * mkdtemp (char *template);
#endif

void attribute_hidden InitTempDir()
{
    char *tmp, *tm, tmp1[PATH_MAX+11], *p;
    int len;
#ifdef Win32
    char tmp2[MAX_PATH];
    int hasspace = 0;
#endif

    if(R_TempDir) return; /* someone else set it */
    tmp = NULL; /* getenv("R_SESSION_TMPDIR");   no longer set in R.sh */
    if (!tmp) {
    tm = getenv("TMPDIR");
    if (!isDir(tm)) {
        tm = getenv("TMP");
        if (!isDir(tm)) {
        tm = getenv("TEMP");
        if (!isDir(tm))
#ifdef Win32
            tm = getenv("R_USER"); /* this one will succeed */
#else
            tm = "/tmp";
#endif
        }
    }
#ifdef Win32
    /* make sure no spaces in path */
    for (p = tm; *p; p++)
        if (isspace(*p)) { hasspace = 1; break; }
    if (hasspace) {
        GetShortPathName(tm, tmp2, MAX_PATH);
        tm = tmp2;
    }
    sprintf(tmp1, "%s\\RtmpXXXXXX", tm);
#else
    sprintf(tmp1, "%s/RtmpXXXXXX", tm);
#endif
    tmp = mkdtemp(tmp1);
    if(!tmp) R_Suicide(_("cannot mkdir R_TempDir"));
#ifndef Win32
# ifdef HAVE_SETENV
    if(setenv("R_SESSION_TMPDIR", tmp, 1))
        errorcall(R_NilValue, _("unable to set R_SESSION_TMPDIR"));
# elif defined(HAVE_PUTENV)
    {
        char * buf = (char *) malloc((strlen(tmp) + 20) * sizeof(char));
        if(buf) {
        sprintf(buf, "R_SESSION_TMPDIR=%s", tmp);
        if(putenv(buf)) 
            errorcall(R_NilValue, _("unable to set R_SESSION_TMPDIR"));
        /* no free here: storage remains in use */
        } else 
        errorcall(R_NilValue, _("unable to set R_SESSION_TMPDIR"));
    }
# endif
#endif
    }

    len = strlen(tmp) + 1;
    p = (char *) malloc(len);
    if(!p)
    R_Suicide(_("cannot allocate R_TempDir"));
    else {
    R_TempDir = p;
    strcpy(R_TempDir, tmp);
    Sys_TempDir = R_TempDir;
    }
}

char * R_tmpnam(const char * prefix, const char * tempdir)
{
    char tm[PATH_MAX], tmp1[PATH_MAX], *res;
    unsigned int n, done = 0;
#ifdef Win32
    char filesep[] = "\\";
#else
    char filesep[] = "/";
#endif

    if(!prefix) prefix = "";    /* NULL */
    if(strlen(tempdir) >= PATH_MAX) error(_("invalid 'tempdir' in R_tmpnam"));
    strcpy(tmp1, tempdir);
    for (n = 0; n < 100; n++) {
    /* try a random number at the end.  Need at least 6 hex digits */
#if RAND_MAX > 16777215
    sprintf(tm, "%s%s%s%x", tmp1, filesep, prefix, rand());
#else
    sprintf(tm, "%s%s%s%x%x", tmp1, filesep, prefix, rand(), rand());
#endif
        if(!R_FileExists(tm)) {
        done = 1;
        break;
    }
    }
    if(!done)
    error(_("cannot find unused tempfile name"));
    res = (char *) malloc((strlen(tm)+1) * sizeof(char));
    strcpy(res, tm);
    return res;
}

SEXP attribute_hidden do_proctime(SEXP call, SEXP op, SEXP args, SEXP env)
#ifdef _R_HAVE_TIMING_
{
    SEXP ans, nm;
    PROTECT(ans = allocVector(REALSXP, 5));
    PROTECT(nm = allocVector(STRSXP, 5));
    R_getProcTime(REAL(ans));
    SET_STRING_ELT(nm, 0, mkChar("user.self"));
    SET_STRING_ELT(nm, 1, mkChar("sys.self"));
    SET_STRING_ELT(nm, 2, mkChar("elapsed"));
    SET_STRING_ELT(nm, 3, mkChar("user.child"));
    SET_STRING_ELT(nm, 4, mkChar("sys.child"));
    setAttrib(ans, R_NamesSymbol, nm);
    setAttrib(ans, R_ClassSymbol, mkString("proc_time"));
    UNPROTECT(2);
    return ans;
}
#else
{
    error(_("proc.time() is not implemented on this system"));
    return R_NilValue;      /* -Wall */
}
#endif