/********************************************************************** * * mexgateway.c * * This file functions as the mex-file entry point. The intended mexnc * operation is gleaned from the first argument, and then we transfer * control to the source file that handles either the NetCDF-2 or * NetCDF-3 API. * *********************************************************************/ /* * $Id: mexgateway.c 2159 2007-03-06 16:50:52Z johnevans007 $ * */ # include # include # include # include # include # include "netcdf.h" # include "mex.h" # include "mexnc.h" /* * Release information. Surely subversion can handle this better than * hardcoding it like this? Isn't there some keyword expansion that * could do it? * */ static char *mexnc_date_id="2007-07-12 (Thu, 12 Jul 2007) $"; static char *mexnc_release_id="MEXNC 2.0.25"; op *opname2opcode ( const char *, int, int ); void get_mexnc_info ( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[], OPCODE ); /****************************************************************************** * * MEXFUNCTION * * Gateway routine for the mex-file. * * PARAMETERS: * nlhs: * Number of output arguments defined in matlab. * plhs: * Array of output matlab arrays. * nrhs: * number of input arguments defined in matlab. * prhs: * Array of input matlab arrays. * ******************************************************************************/ void mexFunction ( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) { /* * Metadata about the requested operation. */ op *nc_op; /* * loop index * */ int j; char error_message[1000]; /* * Disable the NC_FATAL option from ncopts. This is a netcdf-2 thing. * Is this needed anymore? * */ if (ncopts & NC_FATAL) { ncopts -= NC_FATAL; } /* * We need at least one input argument. * */ if (nrhs == 0) { Usage(); return; } /* * Make sure the first argument is not the empty set. * */ if ( mxIsEmpty ( prhs[0] ) ) { mexErrMsgTxt ( "First parameter to mexnc cannot be the empty set.\n" ); } nc_op = opname2opcode ( mxArrayToString(prhs[0]) , nlhs, nrhs ); /* * Now make sure that none of the other arguments are the * empty set. We need to know the name of the netcdf operation * before we can do this, since a few of the netcdf-2 functions * do actually allow for the empty set. If there are any illegal * empty set arguments, then an exception is thrown. * * */ check_other_args_for_empty_set ( nc_op, prhs, nrhs ); /* * Here's the switchyard for all the mexnc routines. */ switch ( nc_op->opcode) { case GET_MEXNC_INFO: plhs[0] = mxCreateString ( mexnc_release_id ); plhs[1] = mxCreateString ( mexnc_date_id ); break; /* * NetCDF-3 stuff. */ case ABORT: handle_nc_abort ( nlhs, plhs, nrhs, prhs, nc_op ); break; case CLOSE: handle_nc_close ( nlhs, plhs, nrhs, prhs, nc_op ); break; case COPY_ATT: handle_nc_copy_att ( nlhs, plhs, nrhs, prhs, nc_op ); break; case _CREATE: handle_nc__create ( nlhs, plhs, nrhs, prhs, nc_op ); break; case CREATE: handle_nc_create ( nlhs, plhs, nrhs, prhs, nc_op ); break; case DEF_DIM: handle_nc_def_dim ( nlhs, plhs, nrhs, prhs, nc_op ); break; case DEF_VAR: handle_nc_def_var ( nlhs, plhs, nrhs, prhs, nc_op ); break; case DEL_ATT: handle_nc_del_att ( nlhs, plhs, nrhs, prhs, nc_op ); break; case _ENDDEF: handle_nc__enddef ( nlhs, plhs, nrhs, prhs, nc_op ); break; /* * This should have just been for ENDDEF. I mistakenly misspelled it, and * then had to support it. */ case END_DEF: case ENDDEF: handle_nc_enddef ( nlhs, plhs, nrhs, prhs, nc_op ); break; case GET_ATT_DOUBLE: case GET_ATT_FLOAT: case GET_ATT_INT: case GET_ATT_SHORT: case GET_ATT_SCHAR: case GET_ATT_UCHAR: case GET_ATT_TEXT: handle_nc_get_att ( nlhs, plhs, nrhs, prhs, nc_op ); break; case GET_VAR_DOUBLE: case GET_VAR_FLOAT: case GET_VAR_INT: case GET_VAR_SHORT: case GET_VAR_SCHAR: case GET_VAR_UCHAR: case GET_VAR_TEXT: case GET_VAR1_DOUBLE: case GET_VAR1_FLOAT: case GET_VAR1_INT: case GET_VAR1_SHORT: case GET_VAR1_SCHAR: case GET_VAR1_UCHAR: case GET_VAR1_TEXT: case GET_VARA_DOUBLE: case GET_VARA_FLOAT: case GET_VARA_INT: case GET_VARA_SHORT: case GET_VARA_SCHAR: case GET_VARA_UCHAR: case GET_VARA_TEXT: case GET_VARS_DOUBLE: case GET_VARS_FLOAT: case GET_VARS_INT: case GET_VARS_SHORT: case GET_VARS_SCHAR: case GET_VARS_UCHAR: case GET_VARS_TEXT: handle_nc_get_var_x ( nlhs, plhs, nrhs, prhs, nc_op ); break; case GET_VARM_DOUBLE: case GET_VARM_FLOAT: case GET_VARM_INT: case GET_VARM_SHORT: case GET_VARM_SCHAR: case GET_VARM_UCHAR: case GET_VARM_TEXT: handle_nc_get_varm_x ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ: handle_nc_inq ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_NDIMS: handle_nc_inq_ndims ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_NVARS: handle_nc_inq_nvars ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_NATTS: handle_nc_inq_natts ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_ATT: handle_nc_inq_att ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_ATTID: handle_nc_inq_attid ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_ATTLEN: handle_nc_inq_attlen ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_ATTNAME: handle_nc_inq_attname ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_ATTTYPE: handle_nc_inq_atttype ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_DIM: handle_nc_inq_dim ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_DIMID: handle_nc_inq_dimid ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_DIMLEN: handle_nc_inq_dimlen ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_DIMNAME: handle_nc_inq_dimname ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_LIBVERS: plhs[0] = mxCreateString ( nc_inq_libvers() ); break; case INQ_VAR: handle_nc_inq_var ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_VARNAME: handle_nc_inq_varname ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_VARTYPE: handle_nc_inq_vartype ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_VARNDIMS: handle_nc_inq_varndims ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_VARDIMID: handle_nc_inq_vardimid ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_VARNATTS: handle_nc_inq_varnatts ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_UNLIMDIM: handle_nc_inq_unlimdim ( nlhs, plhs, nrhs, prhs, nc_op ); break; case INQ_VARID: handle_nc_inq_varid ( nlhs, plhs, nrhs, prhs, nc_op ); break; case _OPEN: handle_nc__open ( nlhs, plhs, nrhs, prhs, nc_op ); break; case OPEN: handle_nc_open ( nlhs, plhs, nrhs, prhs, nc_op ); break; case PUT_ATT_DOUBLE: case PUT_ATT_FLOAT: case PUT_ATT_INT: case PUT_ATT_SHORT: case PUT_ATT_SCHAR: case PUT_ATT_UCHAR: case PUT_ATT_TEXT: handle_nc_put_att ( nlhs, plhs, nrhs, prhs, nc_op ); break; case PUT_VAR_DOUBLE: case PUT_VAR_FLOAT: case PUT_VAR_INT: case PUT_VAR_SHORT: case PUT_VAR_SCHAR: case PUT_VAR_UCHAR: case PUT_VAR_TEXT: case PUT_VAR1_DOUBLE: case PUT_VAR1_FLOAT: case PUT_VAR1_INT: case PUT_VAR1_SHORT: case PUT_VAR1_SCHAR: case PUT_VAR1_UCHAR: case PUT_VAR1_TEXT: case PUT_VARA_DOUBLE: case PUT_VARA_FLOAT: case PUT_VARA_INT: case PUT_VARA_SHORT: case PUT_VARA_SCHAR: case PUT_VARA_UCHAR: case PUT_VARA_TEXT: case PUT_VARS_DOUBLE: case PUT_VARS_FLOAT: case PUT_VARS_INT: case PUT_VARS_SHORT: case PUT_VARS_SCHAR: case PUT_VARS_UCHAR: case PUT_VARS_TEXT: handle_nc_put_var_x ( nlhs, plhs, nrhs, prhs, nc_op ); break; case PUT_VARM_DOUBLE: case PUT_VARM_FLOAT: case PUT_VARM_INT: case PUT_VARM_SHORT: case PUT_VARM_SCHAR: case PUT_VARM_UCHAR: case PUT_VARM_TEXT: handle_nc_put_varm_x ( nlhs, plhs, nrhs, prhs, nc_op ); break; case REDEF: handle_nc_redef ( nlhs, plhs, nrhs, prhs, nc_op ); break; case RENAME_ATT: handle_nc_rename_att ( nlhs, plhs, nrhs, prhs, nc_op ); break; case RENAME_DIM: handle_nc_rename_dim ( nlhs, plhs, nrhs, prhs, nc_op ); break; case RENAME_VAR: handle_nc_rename_var ( nlhs, plhs, nrhs, prhs, nc_op ); break; case SET_FILL: handle_nc_set_fill ( nlhs, plhs, nrhs, prhs, nc_op ); break; case STRERROR: handle_nc_strerror ( nlhs, plhs, nrhs, prhs, nc_op ); break; case SYNC: handle_nc_sync ( nlhs, plhs, nrhs, prhs, nc_op ); break; /* * Ok these are all the NetCDF 2.4 API calls. Keep'em locked * away in the attic. * */ case ATTCOPY: case ATTDEL: case ATTGET: case ATTINQ: case ATTNAME: case ATTRENAME: case ATTPUT: case DIMDEF: case DIMID: case DIMINQ: case DIMRENAME: case ENDEF: case ERR: case INQUIRE: case PARAMETER: case RECPUT: case RECGET: case RECINQ: case SETFILL: case SETOPTS: case TYPELEN: case VARCOPY: case VARDEF: case VARGET: case VARGET1: case VARGETG: case VARID: case VARINQ: case VARPUT: case VARPUT1: case VARPUTG: case VARRENAME: handle_netcdf2_api ( nlhs, plhs, nrhs, prhs, nc_op ); break; default: sprintf ( error_message, "MEXNC ERROR:\n" ); sprintf ( error_message+strlen(error_message), "\tUnhandled opcode %d, %s, line %d, file %s\n", nc_op->opcode, nc_op->opname, __LINE__, __FILE__ ); mexErrMsgTxt ( error_message ); break; } return; } /******************************************************************************* * * OPNAME2OPCODE * * This function transforms the implied name of a netcdf function (such as * "nc_open") into an enumerated type (such as OPEN) that is more readily * handled by switch statements. We also check to make sure that the right * number of inputs and outputs are specified. * * PARAMETERS: * opname: * the name of the netcdf operation, such as GET_VAR_DOUBLE * nlhs, nrhs: * number of left-hand and right-hand arguments that were passed in from * matlab. * * RETURN VALUE: * Pointer to a structure containing metadata about the specified operation. * ******************************************************************************/ op *opname2opcode ( const char *opname, int nlhs, int nrhs ) { OPCODE opcode; int j; /* * Used to access the opcode portions. * */ char *p; char error_message[1000]; /* * The operation name must be converted to lower case. * Initialize it to '\0'. * */ char lcopname[MAX_NC_NAME] = {0}; sprintf ( lcopname, "%s", opname ); /* * Convert the operation name to its opcode. */ for (j = 0; j < strlen(lcopname); j++) { lcopname[j] = (char) tolower((int) lcopname[j]); } p = lcopname; /* * Trim off leading "nc" or "nc_" if it is there */ if (strncmp(p, "nc", 2) == 0) { p += 2; } if (strncmp(p, "nc_", 3) == 0) { p += 3; } /* * Go thru the list of mexnc routines, try to find a match. * */ j = 0; while (ops[j].opcode != NONE) { if ( strcmp(p, ops[j].opname) != 0 ) { /* * This wasn't it. Go on to the next. */ j++; } else { break; } } /* * So did we find a match for the requested operation? */ if (ops[j].opcode == NONE) { sprintf ( error_message, "MEXNC ERROR:\n" ); sprintf ( error_message+strlen(error_message), "\tNo such operation as \"%s\"\n", opname ); mexErrMsgTxt(error_message); } /* * Check that proper number of inputs and outputs has been respected. * */ if (ops[j].nrhs > nrhs) { sprintf ( error_message, "MEXNC ERROR:\n" ); sprintf ( error_message+strlen(error_message), "\t\"%s\" requires at least %d input arguments, you provided %d.\n", opname, ops[j].nrhs, nrhs ); mexErrMsgTxt(error_message); } if (ops[j].nlhs < nlhs) { sprintf ( error_message, "MEXNC ERROR:\n" ); sprintf ( error_message+strlen(error_message), "\t\"%s\" provides at most %d output arguments, you requested %d.\n", opname, ops[j].nlhs, nlhs ); mexErrMsgTxt(error_message); } return ( & ops[j] ); } /******************************************************************************* * * CHECK_OTHER_ARGS_FOR_EMPTY_SET * * After the first argument has been checked to see that it is not the empty * set, the op name can be extracted. If the op name is known, then we can * intelligently check the other input arguments to make sure that none of them * are the empty set except where specifically allowed. The reason for the * allowed cases aren't really explained anywhere. And unfortunately there's * already code out there that makes use of this. Fantastic. * * The empty string is considered different than [], I suppose, so * we skip that. * * PARAMETERS: * nc_op: * Structure of metadata pertaining to the requested mexnc operation. * prhs: * Array of right-hand side arguments passed in from matlab. * nrhs: * Number of right-hand side arguments. * * RETURN VALUE: * None. * ******************************************************************************/ void check_other_args_for_empty_set ( op *nc_op, const mxArray *prhs[], int nrhs ) { /* * If we encounter the empty set where it is illegal, say so here. * */ char error_msg[500]; /* * Loop index for matlab array inputs. * */ int i; for ( i = 1; i < nrhs; ++i ) { int not_ok = 1; /* * If an argument is empty, check to see if it isn't one of the * allowed cases. */ if ( mxIsEmpty(prhs[i]) ) { /* * These cases are allowed. */ /* * Causes the creation of a zero-length character attribute. */ if ( ( nc_op->opcode == ATTPUT ) && ( i == 6 ) ) { not_ok = 0; } /* * DEF_VAR and VARDEF allow for the creation of a singleton * variable. The empty set in this case is the array of * dimension IDs. */ if ( ( nc_op->opcode == DEF_VAR ) && ( i == 5 ) ) { not_ok = 0; } if ( ( nc_op->opcode == VARDEF ) && ( i == 5 ) ) { not_ok = 0; } /* * The C API for ncvargetg and ncvarputg allow for an "imap" * parameter that specifies an "in-memory" mapping. * This is not used in the C API. The documentation was * unfortunately written to suggest usage of the empty set, * however, so we have to allow for that here. */ if ( ( nc_op->opcode == VARGETG ) && ( i == 6 ) ) { not_ok = 0; } if ( ( nc_op->opcode == VARPUTG ) && ( i == 6 ) ) { not_ok = 0; } if ( ( nc_op->opcode == VARPUTG ) && ( i == 7 ) ) { not_ok = 0; } if ( ( nc_op->opcode == VARPUT1 ) && ( i == 4 ) ) { not_ok = 0; } if ( ( nc_op->opcode == VARPUT ) && ( i == 5 ) ) { not_ok = 0; } /* * An empty string is sometimes allowed. * */ if ( mxIsChar ( prhs[i] ) ) { not_ok = 0; } } else { not_ok = 0; } if ( not_ok ) { sprintf ( error_msg, "%s: cannot have empty set in input position %d.\n", nc_op->opname, i+1 ); mexErrMsgTxt ( error_msg ); } } }