/* * R.app : a Cocoa front end to: "R A Computer Language for Statistical Data Analysis" * * R.app Copyright notes: * Copyright (C) 2005 The R Foundation * written by Stefano M. Iacus and Simon Urbanek * * * R Copyright notes: * Copyright (C) 1995-1996 Robert Gentleman and Ross Ihaka * Copyright (C) 1998-2001 The R Development Core Team * Copyright (C) 2002-2005 The R Foundation * * 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. * * A copy of the GNU General Public License is available via WWW at * http://www.gnu.org/copyleft/gpl.html. You can also obtain it by * writing to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston, MA 02111-1307 USA. * * Created by Simon Urbanek on Tue Jul 13 2004. * */ /* #include "privateR.h" */ #include #include #include #include #include #include #include #include #include #include #include #include #import "REngine.h" /* any subsequent calls of ProcessEvents within the following time slice are ignored (in ms) */ #define MIN_DELAY_BETWEEN_EVENTS_MS 150 /* localization - we don't want to include GUI specific includes, so we define it manually */ #ifdef NLS #undef NLS #endif #ifdef NLSC #undef NLSC #endif #define NLS(S) NSLocalizedString(S,@"") #define NLSC(S,C) NSLocalizedString(S,C) #ifndef SLog #if defined DEBUG_RGUI && defined PLAIN_STDERR #define SLog(X,...) NSLog(X, ## __VA_ARGS__) #else #define SLog(X,...) #endif #endif /* for compatibility with R registration - unused here */ #define checkArity(X,Y) /* we have no access to config.h, so for the moment, let's disable i18n on C level - our files aren't even precessed by R anyway. */ #ifdef _ #undef _ #endif #define _(A) (A) int insideR = 0; /* from Defn.h */ extern Rboolean R_Interactive; /* TRUE during interactive use*/ extern FILE* R_Consolefile; /* Console output file */ extern FILE* R_Outputfile; /* Output file */ /* from src/unix/devUI.h */ extern void (*ptr_R_Suicide)(char *); extern void (*ptr_R_ShowMessage)(); extern int (*ptr_R_ReadConsole)(char *, unsigned char *, int, int); extern void (*ptr_R_WriteConsole)(char *, int); extern void (*ptr_R_ResetConsole)(); extern void (*ptr_do_flushconsole)(); extern void (*ptr_R_ClearerrConsole)(); extern void (*ptr_R_Busy)(int); /* extern void (*ptr_R_CleanUp)(SA_TYPE, int, int); */ //extern int (*ptr_R_ShowFiles)(int, char **, char **, char *, Rboolean, char *); //extern int (*ptr_R_EditFiles)(int, char **, char **, char *); extern int (*ptr_R_ChooseFile)(int, char *, int); extern void (*ptr_R_loadhistory)(SEXP, SEXP, SEXP, SEXP); extern void (*ptr_R_savehistory)(SEXP, SEXP, SEXP, SEXP); //extern void (*ptr_R_StartCocoaRL)(); void Re_WritePrompt(char *prompt) { NSString *s = [[NSString alloc] initWithUTF8String: prompt]; insideR--; [[REngine mainHandler] handleWritePrompt:s]; [s release]; insideR++; } static long lastProcessEvents=0; void Re_ProcessEvents(void){ struct timeval rv; if (!gettimeofday(&rv,0)) { long curTime = (rv.tv_usec/1000)+(rv.tv_sec&0x1fffff)*1000; if (curTime - lastProcessEvents < MIN_DELAY_BETWEEN_EVENTS_MS) return; } #ifdef USE_POOLS NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; #endif if ([[REngine mainEngine] allowEvents]) // if events are masked, we won't call the handler. we may re-think what we do about the timer, though ... [[REngine mainHandler] handleProcessEvents]; if (!gettimeofday(&rv,0)) // use the exit time for the measurement of next events - handleProcessEvents may take long lastProcessEvents = (rv.tv_usec/1000)+(rv.tv_sec&0x1fffff)*1000; #ifdef USE_POOLS [pool release]; #endif } static char *readconsBuffer=0; static char *readconsPos=0; int Re_ReadConsole(char *prompt, unsigned char *buf, int len, int addtohistory) { insideR--; Re_WritePrompt(prompt); if (!readconsBuffer) { char *newc = [[REngine mainHandler] handleReadConsole: addtohistory]; if (!newc) { insideR++; return 0; } readconsPos=readconsBuffer=newc; } if (readconsBuffer) { int skipPC=0; char *c = readconsPos; while (*c && *c!='\n' && *c!='\r') c++; if (*c=='\r') { /* convert PC and Mac endings to unix */ *c='\n'; if (c[1]=='\n') skipPC=1; } if (*c) c++; /* if not at the end, point past the content to use */ if (c-readconsPos>=len) c=readconsPos+(len-1); memcpy(buf, readconsPos, c-readconsPos); buf[c-readconsPos]=0; if (skipPC) c++; if (*c) readconsPos=c; else readconsPos=readconsBuffer=0; [[REngine mainHandler] handleProcessingInput: (char*) buf]; insideR=YES; return 1; } return 0; } void Re_RBusy(int which) { insideR--; [[REngine mainHandler] handleBusy: (which==0)?NO:YES]; insideR++; } void Re_WriteConsoleEx(char *buf, int len, int otype) { NSString *s = nil; if (buf[len]) { /* well, this is an ultima ratio, we are assuming null-terminated string, but one never knows ... */ char *c = (char*) malloc(len+1); memcpy(c, buf, len); c[len]=0; s = [[NSString alloc] initWithUTF8String:c]; free(c); } else s = [[NSString alloc] initWithUTF8String:buf]; if (!s) { SLog(@"Rcallbacks:Re_WriteConsole: suspicious string of length %d doesn't parse as UTF8. Will use raw cString.", len); s = [[NSString alloc] initWithCString:buf length:len]; SLog(@"Rcallbacks:Re_WriteConsole: string parsed as \"%@\"", s); } if (s) { [[REngine mainHandler] handleWriteConsole: s withType: otype]; [s release]; } } void Re_WriteConsole(char *buf, int len) { Re_WriteConsoleEx(buf, len, 0); } /* Indicate that input is coming from the console */ void Re_ResetConsole() { } /* Stdio support to ensure the console file buffer is flushed */ void Re_FlushConsole() { insideR--; [[REngine mainHandler] handleFlushConsole]; insideR++; } /* Reset stdin if the user types EOF on the console. */ void Re_ClearerrConsole() { } int Re_ChooseFile(int new, char *buf, int len) { int r; insideR--; r=[[REngine mainHandler] handleChooseFile: buf len:len isNew:new]; insideR++; return r; } void Re_ShowMessage(char *buf) { insideR--; [[REngine mainHandler] handleShowMessage: buf]; insideR++; } int Re_Edit(char *file){ int r; insideR--; r=[[REngine mainHandler] handleEdit: file]; insideR++; return r; } int Re_EditFiles(int nfile, char **file, char **wtitle, char *pager){ int r; insideR--; r = [[REngine mainHandler] handleEditFiles: nfile withNames: file titles: wtitle pager: pager]; insideR++; return r; } int Re_ShowFiles(int nfile, char **file, char **headers, char *wtitle, Rboolean del, char *pager) { int r; insideR--; r = [[REngine mainHandler] handleShowFiles: nfile withNames: file headers: headers windowTitle: wtitle pager: pager andDelete: del]; insideR++; return r; } //==================================================== the following callbacks are Cocoa-specific callbacks (see CocoaHandler) int Re_system(char *cmd) { int r; insideR--; if ([REngine cocoaHandler]) r = [[REngine cocoaHandler] handleSystemCommand: cmd]; else { // fallback in case there's no handler // reset signal handlers signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGALRM, SIG_DFL); signal(SIGCHLD, SIG_DFL); r = system(cmd); } insideR++; return r; } int Re_CustomPrint(char *type, SEXP obj) { insideR--; if ([REngine cocoaHandler]) { RSEXP *par = [[RSEXP alloc] initWithSEXP: obj]; int res = [[REngine cocoaHandler] handleCustomPrint: type withObject: par]; [par release]; insideR++; return res; } insideR++; return -1; } SEXP Re_packagemanger(SEXP call, SEXP op, SEXP args, SEXP env) { SEXP pkgname, pkgstatus, pkgdesc, pkgurl; char *vm; SEXP ans; int i, len; char **sName, **sDesc, **sURL; BOOL *bStat; checkArity(op, args); if (![REngine cocoaHandler]) return R_NilValue; vm = vmaxget(); pkgstatus = CAR(args); args = CDR(args); pkgname = CAR(args); args = CDR(args); pkgdesc = CAR(args); args = CDR(args); pkgurl = CAR(args); args = CDR(args); if(!isString(pkgname) || !isLogical(pkgstatus) || !isString(pkgdesc) || !isString(pkgurl)) errorcall(call, "invalid arguments"); len = LENGTH(pkgname); if (len!=LENGTH(pkgstatus) || len!=LENGTH(pkgdesc) || len!=LENGTH(pkgurl)) errorcall(call, "invalid arguments (length mismatch)"); if (len==0) { insideR--; [[REngine cocoaHandler] handlePackages: 0 withNames: 0 descriptions: 0 URLs: 0 status: 0]; insideR++; vmaxset(vm); return pkgstatus; } sName = (char**) malloc(sizeof(char*)*len); sDesc = (char**) malloc(sizeof(char*)*len); sURL = (char**) malloc(sizeof(char*)*len); bStat = (BOOL*) malloc(sizeof(BOOL)*len); i = 0; // we don't copy since the Obj-C side is responsible for making copies if necessary while (i