Hurray! An R Connections API!

I waited until April 3 to post this, so it wouldn't be taken as an April Fool's joke! A recent R News item announces that we now have a bona fide mechanism to create custom connections in R! This makes it possible to implement a custom connection in an R package. Until now, the only feasible alternative was to write a patch (but see also this trick), as I had done to implement a tty connection! Of course, this was a suboptimal solution that required an update for each new version of R. In fact, my last update was for R v2.13.1. This new development will make it much easier to share this code. I will also need to update my first draft of R Connection Internals, which is now three years old. Perhaps the document is better suited as a package vignette. More to follow later...

So that we know who to thank:

matt@deb6box$ svn log -r 62016 http://svn.r-project.org/R/trunk/src/include/R_ext/Connections.h
------------------------------------------------------------------------
r62016 | urbaneks | 2013-02-21 14:29:44 -0500 (Thu, 21 Feb 2013) | 1 line

add API to create custom connections
------------------------------------------------------------------------

Here is the header file itself, with credits. It looks like all of the Rconnection struct is made available. But, notice the warning!

matt@deb6box$ svn blame -r 62016 http://svn.r-project.org/R/trunk/src/include/R_ext/Connections.h
 11656     ripley /*
 11656     ripley  *  R : A Computer Language for Statistical Data Analysis
 62016   urbaneks  *  Copyright (C) 2000-2013   The R Core Team.
 11656     ripley  *
 11656     ripley  *  This program is free software; you can redistribute it and/or modify
 11656     ripley  *  it under the terms of the GNU General Public License as published by
 11656     ripley  *  the Free Software Foundation; either version 2 of the License, or
 11656     ripley  *  (at your option) any later version.
 11656     ripley  *
 11656     ripley  *  This program is distributed in the hope that it will be useful,
 11656     ripley  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 11656     ripley  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11656     ripley  *  GNU General Public License for more details.
 11656     ripley  *
 11656     ripley  *  You should have received a copy of the GNU General Public License
 42308     ripley  *  along with this program; if not, a copy is available at
 42308     ripley  *  http://www.r-project.org/Licenses/
 11656     ripley  */
 11656     ripley 
 62016   urbaneks #ifndef R_EXT_CONNECTIONS_H_
 62016   urbaneks #define R_EXT_CONNECTIONS_H_
 62016   urbaneks 
 11668     ripley #include 
 11668     ripley 
 62016   urbaneks #ifndef NO_C_HEADERS
 62016   urbaneks # include  /* for size_t */
 62016   urbaneks # include  /* for va_list */
 42677   urbaneks #endif
 42677   urbaneks 
 62016   urbaneks /* IMPORTANT: we do not expect future connection APIs to be
 62016   urbaneks    backward-compatible so if you use this, you *must* check the version
 62016   urbaneks    and proceed only if it matches what you expect
 62016   urbaneks 
 62016   urbaneks    We explicitly reserve the right to change the connection
 62016   urbaneks    implementation without a compatibility layer.
 62016   urbaneks  */
 62016   urbaneks #define R_CONNECTIONS_VERSION 1
 62016   urbaneks 
 45984     ripley /* this allows the opaque pointer definition to be made available 
 44013     ripley    in Rinternals.h */
 16472       luke #ifndef HAVE_RCONNECTION_TYPEDEF
 16472       luke typedef struct Rconn  *Rconnection;
 16472       luke #endif
 16472       luke struct Rconn {
 11656     ripley     char* class;
 11656     ripley     char* description;
 44013     ripley     int enc; /* the encoding of 'description' */
 11656     ripley     char mode[5];
 23228     ripley     Rboolean text, isopen, incomplete, canread, canwrite, canseek, blocking, 
 23228     ripley 	isGzcon;
 18583     ripley     Rboolean (*open)(struct Rconn *);
 11656     ripley     void (*close)(struct Rconn *); /* routine closing after auto open */
 11656     ripley     void (*destroy)(struct Rconn *); /* when closing connection */
 11656     ripley     int (*vfprintf)(struct Rconn *, const char *, va_list);
 11656     ripley     int (*fgetc)(struct Rconn *);
 32497     ripley     int (*fgetc_internal)(struct Rconn *);
 31166     ripley     double (*seek)(struct Rconn *, double, int, int);
 13305     ripley     void (*truncate)(struct Rconn *);
 11656     ripley     int (*fflush)(struct Rconn *);
 11656     ripley     size_t (*read)(void *, size_t, size_t, struct Rconn *);
 11656     ripley     size_t (*write)(const void *, size_t, size_t, struct Rconn *);
 59167     ripley     int nPushBack, posPushBack; /* number of lines, position on top line */
 11656     ripley     char **PushBack;
 12256         pd     int save, save2;
 32492     ripley     char encname[101];
 32492     ripley     /* will be iconv_t, which is a pointer. NULL if not in use */
 32492     ripley     void *inconv, *outconv;
 32497     ripley     /* The idea here is that no MBCS char will ever not fit */
 32497     ripley     char iconvbuff[25], oconvbuff[50], *next, init_out[25];
 32497     ripley     short navail, inavail;
 32497     ripley     Rboolean EOF_signalled;
 44101     ripley     Rboolean UTF8out;
 41765     ripley     void *id;
 41765     ripley     void *ex_ptr;
 11656     ripley     void *private;
 61527     ripley     int status; /* for pipes etc */
 16472       luke };
 11656     ripley 
 62016   urbaneks #ifdef  __cplusplus
 62016   urbaneks extern "C" {
 62016   urbaneks #endif
 11656     ripley 
 62016   urbaneks SEXP   R_new_custom_connection(const char *description, const char *mode, const char *class_name, Rconnection *ptr);
 62016   urbaneks size_t R_ReadConnection(Rconnection con, void *buf, size_t n);
 62016   urbaneks size_t R_WriteConnection(Rconnection con, void *buf, size_t n);
 13366     ripley 
 62016   urbaneks #ifdef  __cplusplus
 62016   urbaneks }
 25961     ripley #endif
 25961     ripley 
 62016   urbaneks #endif