示例#1
0
aed_sct * /* O [sct] List of attribute edit structures */
nco_prs_aed_lst /* [fnc] Parse user-specified attribute edits into structure list */
(const int nbr_aed, /* I [nbr] Number of attributes in list */
 X_CST_PTR_CST_PTR_Y(char,aed_arg)) /* I/O [sng] List of user-specified attribute edits (delimiters are changed to NULL on output */
{
  /* Purpose: Parse name, type, size, and value elements of comma-separated list of attribute edit information
     Routine merely evaluates syntax of input expressions
     Routine does not validate attributes or variables against those present in input netCDF file */

  /* Options are:
     -a att_nm,var_nm,mode,att_typ,att_val (modifies attribute att_nm for the single variable var_nm)

     -a att_nm,,mode,att_typ,att_val (modifies attribute att_nm for every variable in file)
     If option -a is given with var_nm = NULL, then var_nm is expanded into every variable name in file
     Thus attribute editing operation is performed on every variable in file.

     mode,att_nm,att_typ,att_val (modifies global attribute att_nm for file)
     This option may be combined with modes -a, -c, -d, or -o to specify 
     appending to, changing, deleting, or overwriting, any existing global attribute named att_nm

     One mode must be set for each edited attribute: append (a), create (c), delete (d), modify (m), or overwrite (o).
     -a: Attribute append mode
     Append value att_val to current var_nm attribute att_nm value att_val, if any. 
     If var_nm does not have an attribute att_nm, there is not effect.

     -c: Attribute create mode
     Create variable var_nm attribute att_nm with att_val if att_nm does not yet exist. 
     If var_nm already has an attribute att_nm, there is not effect.

     -d: Attribute delete mode
     Delete current var_nm attribute att_nm.
     If var_nm does not have an attribute att_nm, there is not effect.

     -m: Attribute modify mode
     Change value of current var_nm attribute att_nm to value att_val.
     If var_nm does not have an attribute att_nm, there is not effect.

     -o: Attribute overwrite mode
     Write attribute att_nm with value att_val to variable var_nm, overwriting existing attribute att_nm, if any.
     This is default mode.
   */

  char **arg_lst;

  const char * const dlm_sng=",";

  const long idx_att_val_arg=4L; /* Number of required delimiters preceding attribute values in -a argument list */

  aed_sct *aed_lst;

  int idx;
  int arg_nbr;

  aed_lst=(aed_sct *)nco_malloc(nbr_aed*sizeof(aed_sct));

  for(idx=0;idx<nbr_aed;idx++){

    /* Process attribute edit specifications as normal text list */
    arg_lst=nco_lst_prs_2D(aed_arg[idx],dlm_sng,&arg_nbr);

    /* Check syntax */
    if(
       arg_nbr < 5 || /* Need more info */
       /* arg_lst[0] == NULL || */ /* att_nm not specified */
       arg_lst[2] == NULL || /* mode not specified */
       (*(arg_lst[2]) != 'd' && (arg_lst[3] == NULL || (arg_lst[idx_att_val_arg] == NULL && *(arg_lst[3]) != 'c'))) || /* att_typ and att_val must be specified when mode is not delete, except that att_val = "" is valid for character type */
       False){
      (void)fprintf(stdout,"%s: ERROR in attribute edit specification %s\n",prg_nm_get(),aed_arg[idx]);
      nco_exit(EXIT_FAILURE);
    } /* end if */

    /* Initialize structure */
    /* aed strings not explicitly set by user remain NULL,
       i.e., specifying default setting appears as if nothing was set.
       Hopefully, in routines that follow, the branch followed by an aed for which
       all default settings were specified (e.g.,"-a foo,,,,") will yield same result
       as branch for which all defaults were set. */
    aed_lst[idx].att_nm=NULL;
    aed_lst[idx].var_nm=NULL;
    aed_lst[idx].val.vp=NULL;
    aed_lst[idx].type=NC_CHAR;
    aed_lst[idx].mode=aed_overwrite;
    aed_lst[idx].sz=-1L;
    aed_lst[idx].id=-1;

    /* Fill in structure */
    if(arg_lst[0]) aed_lst[idx].att_nm=strdup(arg_lst[0]);
    if(arg_lst[1]) aed_lst[idx].var_nm=strdup(arg_lst[1]);

    /* fxm: Change these switches to string comparisons someday */
    /* Set mode of current aed structure */
    /* Convert single letter code to mode enum */
    /*    if(!strcmp("append",arg_lst[2])){aed_lst[idx].mode=aed_append;
    }else if(!strcmp("create",arg_lst[2])){aed_lst[idx].mode=aed_create;
    }else if(!strcmp("delete",arg_lst[2])){aed_lst[idx].mode=aed_delete;
    }else if(!strcmp("modify",arg_lst[2])){aed_lst[idx].mode=aed_modify;
    }else if(!strcmp("overwrite",arg_lst[2])){aed_lst[idx].mode=aed_overwrite;} */
    switch(*(arg_lst[2])){
    case 'a': aed_lst[idx].mode=aed_append; break;
    case 'c': aed_lst[idx].mode=aed_create; break;
    case 'd': aed_lst[idx].mode=aed_delete; break;
    case 'm': aed_lst[idx].mode=aed_modify; break;
    case 'o': aed_lst[idx].mode=aed_overwrite; break;
    default: 
      (void)fprintf(stderr,"%s: ERROR `%s' is not a supported mode\n",prg_nm_get(),arg_lst[2]);
      (void)fprintf(stderr,"%s: HINT: Valid modes are `a' = append, `c' = create,`d' = delete, `m' = modify, and `o' = overwrite",prg_nm_get());
      nco_exit(EXIT_FAILURE);
      break;
    } /* end switch */

    /* Attribute type and value do not matter if we are deleting it */
    if(aed_lst[idx].mode != aed_delete){

      /* Set type of current aed structure */
      /* Convert single letter code to type enum */
      switch(*(arg_lst[3])){
      case 'f':	aed_lst[idx].type=(nc_type)NC_FLOAT; break;
      case 'd':	aed_lst[idx].type=(nc_type)NC_DOUBLE; break;
      case 'l':	
      case 'i':	aed_lst[idx].type=(nc_type)NC_INT; break;
      case 's':	aed_lst[idx].type=(nc_type)NC_SHORT; break;
      case 'c':	aed_lst[idx].type=(nc_type)NC_CHAR; break;
      case 'b':	aed_lst[idx].type=(nc_type)NC_BYTE; break;
      default: 
	if(!strcasecmp(arg_lst[3],"ub")) aed_lst[idx].type=(nc_type)NC_UBYTE; 
	else if(!strcasecmp(arg_lst[3],"us")) aed_lst[idx].type=(nc_type)NC_USHORT; 
	else if(!strcasecmp(arg_lst[3],"u") || !strcasecmp(arg_lst[3],"ui") || !strcasecmp(arg_lst[3],"ul")) aed_lst[idx].type=(nc_type)NC_UINT; 
	else if(!strcasecmp(arg_lst[3],"ll") || !strcasecmp(arg_lst[3],"int64")) aed_lst[idx].type=(nc_type)NC_INT64; 
	else if(!strcasecmp(arg_lst[3],"ull") || !strcasecmp(arg_lst[3],"uint64")) aed_lst[idx].type=(nc_type)NC_UINT64; 
	else if(!strcasecmp(arg_lst[3],"sng")) aed_lst[idx].type=(nc_type)NC_STRING; 
	else{
	  (void)fprintf(stderr,"%s: ERROR `%s' is not a supported netCDF data type\n",prg_nm_get(),arg_lst[3]);
	  (void)fprintf(stderr,"%s: HINT: Valid data types are `c' = char, `f' = float, `d' = double,`s' = short, 'l' = `i' = integer, `b' = byte",prg_nm_get());
	  nco_exit(EXIT_FAILURE);} /*  end if error */
      break;
      } /* end switch */
      
      /* Re-assemble string list values which inadvertently contain delimiters */
      if(aed_lst[idx].type == NC_CHAR && arg_nbr > idx_att_val_arg+1){
	/* Number of elements which must be concatenated into single string value */
	long lmn_nbr;
	lmn_nbr=arg_nbr-idx_att_val_arg; 
	if(dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stdout,"%s: WARNING NC_CHAR (string) attribute is embedded with %li literal element delimiters (\"%s\"), re-assembling...\n",prg_nm_get(),lmn_nbr-1L,dlm_sng);
	/* Rewrite list, splicing in original delimiter string */
	/* fxm: TODO nco527 ncatted memory may be lost here */
	arg_lst[idx_att_val_arg]=sng_lst_cat(arg_lst+idx_att_val_arg,lmn_nbr,dlm_sng);
	/* Keep bookkeeping straight, just in case */
	arg_nbr=idx_att_val_arg+1L;
	lmn_nbr=1L;
      } /* endif arg_nbr > idx_att_val_arg+1 */
      
      /* Replace any C language '\X' escape codes with ASCII bytes */
      if(aed_lst[idx].type == NC_CHAR) (void)sng_ascii_trn(arg_lst[idx_att_val_arg]);

      /* Set size of current aed structure */
      if(aed_lst[idx].type == NC_CHAR){
	/* 20100409 Remove extra space formerly allocated for NUL-terminator 
	   This caused each append to insert a NUL at end of NC_CHAR attributes
	   Multiple appends would then result in attributes pockmarked with NULs
	   Solves TODO nco985
	   Not yet sure there are no ill side-effects though... */ 
	aed_lst[idx].sz=(arg_lst[idx_att_val_arg] == NULL) ? 0L : strlen(arg_lst[idx_att_val_arg]);
      }else{
	/* Number of elements of numeric types is determined by number of delimiters */
	aed_lst[idx].sz=arg_nbr-idx_att_val_arg;
      } /* end else */
      
      /* Set value of current aed structure */
      if(aed_lst[idx].type == NC_CHAR){
	aed_lst[idx].val.cp=(nco_char *)strdup(arg_lst[idx_att_val_arg]);
      }else{
	double *val_arg_dbl=NULL_CEWI;
	long long *val_arg_lng_lng=NULL_CEWI;
	unsigned long long *val_arg_ulng_lng=NULL_CEWI;
	
	long lmn;
	
	aed_lst[idx].val.vp=(void *)nco_malloc(aed_lst[idx].sz*nco_typ_lng(aed_lst[idx].type));
	
	/* Use type-appropriate conversion */
	switch(aed_lst[idx].type){
	case NC_FLOAT: 
	case NC_DOUBLE: 
	  val_arg_dbl=(double *)nco_malloc(aed_lst[idx].sz*sizeof(double));
	  for(lmn=0L;lmn<aed_lst[idx].sz;lmn++){val_arg_dbl[lmn]=strtod(arg_lst[idx_att_val_arg+lmn],(char **)NULL);} break; 
	case NC_BYTE:
	case NC_INT: 
	case NC_SHORT: 
	case NC_INT64: 
	  val_arg_lng_lng=(long long *)nco_malloc(aed_lst[idx].sz*sizeof(long long));
	  for(lmn=0L;lmn<aed_lst[idx].sz;lmn++){val_arg_lng_lng[lmn]=strtoll(arg_lst[idx_att_val_arg+lmn],(char **)NULL,10);} break;
	case NC_CHAR:
	case NC_UBYTE: 
	case NC_USHORT: 
	case NC_UINT: 
	case NC_UINT64: 
	  val_arg_ulng_lng=(unsigned long long *)nco_malloc(aed_lst[idx].sz*sizeof(unsigned long long));
	  for(lmn=0L;lmn<aed_lst[idx].sz;lmn++){val_arg_ulng_lng[lmn]=strtoull(arg_lst[idx_att_val_arg+lmn],(char **)NULL,10);} break;
	case NC_STRING: break;
	default: nco_dfl_case_nc_type_err(); break;
	} /* end switch */
	
	/* Copy and typecast entire array of values, using implicit coercion rules of C */
	/* 20011001: Use explicit coercion rules to quiet C++ compiler warnings */
	switch(aed_lst[idx].type){
	case NC_FLOAT: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.fp[lmn]=(float)val_arg_dbl[lmn];} break; 
	case NC_DOUBLE: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.dp[lmn]=(double)val_arg_dbl[lmn];} break; 
	case NC_INT: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.ip[lmn]=(nco_int)val_arg_lng_lng[lmn];} break; 
	case NC_SHORT: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.sp[lmn]=(nco_short)val_arg_lng_lng[lmn];} break; 
	case NC_CHAR: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.cp[lmn]=(nco_char)val_arg_ulng_lng[lmn];} break; 
	case NC_BYTE: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.bp[lmn]=(nco_byte)val_arg_lng_lng[lmn];} break; 
	case NC_UBYTE: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.ubp[lmn]=(nco_ubyte)val_arg_ulng_lng[lmn];} break; 
	case NC_USHORT: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.usp[lmn]=(nco_ushort)val_arg_ulng_lng[lmn];} break; 
	case NC_UINT: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.uip[lmn]=(nco_uint)val_arg_ulng_lng[lmn];} break; 
	case NC_INT64: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.i64p[lmn]=(nco_int64)val_arg_lng_lng[lmn];} break; 
	case NC_UINT64: for(lmn=0L;lmn<aed_lst[idx].sz;lmn++) {aed_lst[idx].val.ui64p[lmn]=(nco_uint64)val_arg_ulng_lng[lmn];} break; 
	case NC_STRING: break;
	default: nco_dfl_case_nc_type_err(); break;
	} /* end switch */
	
	/* Free array used to hold input values */
	if(val_arg_dbl) val_arg_dbl=(double *)nco_free(val_arg_dbl);
	if(val_arg_lng_lng) val_arg_lng_lng=(long long *)nco_free(val_arg_lng_lng);
	if(val_arg_ulng_lng) val_arg_ulng_lng=(unsigned long long *)nco_free(val_arg_ulng_lng);
      } /* end else */
      /* Un-typecast pointer to values after access */
      (void)cast_nctype_void(aed_lst[idx].type,&aed_lst[idx].val);
      
    } /* end if mode is not delete */

    arg_lst=nco_sng_lst_free(arg_lst,arg_nbr);

  } /* end loop over aed */
  
  if(dbg_lvl_get() >= nco_dbg_io){
    for(idx=0;idx<nbr_aed;idx++){
      (void)fprintf(stderr,"aed_lst[%d].att_nm = %s\n",idx,aed_lst[idx].att_nm);
      (void)fprintf(stderr,"aed_lst[%d].var_nm = %s\n",idx,aed_lst[idx].var_nm == NULL ? "NULL" : aed_lst[idx].var_nm);
      (void)fprintf(stderr,"aed_lst[%d].id = %i\n",idx,aed_lst[idx].id);
      (void)fprintf(stderr,"aed_lst[%d].sz = %li\n",idx,aed_lst[idx].sz);
      (void)fprintf(stderr,"aed_lst[%d].type = %s\n",idx,nco_typ_sng(aed_lst[idx].type));
      /*      (void)fprintf(stderr,"aed_lst[%d].val = %s\n",idx,aed_lst[idx].val);*/
      (void)fprintf(stderr,"aed_lst[%d].mode = %i\n",idx,aed_lst[idx].mode);
    } /* end loop over idx */
  } /* end debug */
  
  return aed_lst;
  
} /* end nco_prs_aed_lst() */
示例#2
0
文件: nco_scl_utl.c 项目: hdfeos/nco
var_sct * /* O [sct] Output netCDF variable structure representing val */
scl_mk_var /* [fnc] Convert scalar value of any type into NCO variable */
(val_unn val, /* I [frc] Scalar value to turn into netCDF variable */
 const nc_type val_typ) /* I [enm] netCDF type of value */
{
    /* Purpose: Turn scalar value of any type into NCO variable
       Routine is just a wrapper for scl_ptr_mk_var()
       This routine creates the void * argument needed for scl_ptr_mk_var(),
       calls, scl_ptr_mk_var(), then passes back the result */

    var_sct *var;
    ptr_unn val_ptr_unn; /* [ptr] void pointer to value */

    switch(val_typ) {
    case NC_FLOAT:
        val_ptr_unn.fp=&val.f;
        break;
    case NC_DOUBLE:
        val_ptr_unn.dp=&val.d;
        break;
    case NC_INT:
        val_ptr_unn.ip=&val.i;
        break;
    case NC_SHORT:
        val_ptr_unn.sp=&val.s;
        break;
    case NC_CHAR:
        val_ptr_unn.cp=&val.c;
        break;
    case NC_BYTE:
        val_ptr_unn.bp=&val.b;
        break;
    case NC_UBYTE:
        val_ptr_unn.ubp=&val.ub;
        break;
    case NC_USHORT:
        val_ptr_unn.usp=&val.us;
        break;
    case NC_UINT:
        val_ptr_unn.uip=&val.ui;
        break;
    case NC_INT64:
        val_ptr_unn.i64p=&val.i64;
        break;
    case NC_UINT64:
        val_ptr_unn.ui64p=&val.ui64;
        break;
    case NC_STRING:
        val_ptr_unn.sngp=&val.sng;
        break;
    default:
        nco_dfl_case_nc_type_err();
        break;
    } /* end switch */

    /* Un-typecast pointer to values after access */
    (void)cast_nctype_void(val_typ,&val_ptr_unn);

    var=scl_ptr_mk_var(val_ptr_unn,val_typ);

    return var;
} /* end scl_mk_var() */
示例#3
0
void
nco_aed_prc /* [fnc] Process single attribute edit for single variable */
(const int nc_id, /* I [id] Input netCDF file ID */
 const int var_id, /* I [id] ID of variable on which to perform attribute editing */
 const aed_sct aed) /* I [sct] Structure containing information necessary to edit */
{
  /* Purpose: Process single attribute edit for single variable */
  
  /* If var_id == NC_GLOBAL ( = -1) then global attribute will be edited */

#ifdef NCO_NETCDF4_AND_FILLVALUE
  char att_nm_tmp[]="eulaVlliF_"; /* String of same length as "_FillValue" for name hack with netCDF4 */
  nco_bool flg_netCDF4=False; /* [flg] File format is netCDF4 */
#endif /* !NCO_NETCDF4_AND_FILLVALUE */

  char att_nm[NC_MAX_NAME];
  char var_nm[NC_MAX_NAME];
  
  /* NB: netCDF2 specifies att_sz is type int, netCDF3 and netCDF4 use size_t */
  int nbr_att; /* [nbr] Number of attributes */
  int rcd=NC_NOERR; /* [rcd] Return code */
  long att_sz;
     
  nc_type att_typ;
  
  void *att_val_new=NULL;

  if(var_id == NC_GLOBAL){
    /* Get number of global attributes in file */
    (void)nco_inq(nc_id,(int *)NULL,(int *)NULL,&nbr_att,(int *)NULL);
    (void)strcpy(var_nm,"Global");
  }else{
    /* Get name and number of attributes for variable */
    (void)nco_inq_var(nc_id,var_id,var_nm,(nc_type *)NULL,(int *)NULL,(int *)NULL,&nbr_att);
  } /* end else */

  /* Query attribute metadata when attribute name was specified */
  if(aed.att_nm) rcd=nco_inq_att_flg(nc_id,var_id,aed.att_nm,&att_typ,&att_sz);

  /* Before changing metadata, change missing values to new missing value if warranted 
     This capability is add-on feature not implemented too cleanly or efficiently
     If every variable has "_FillValue" attribute and "_FillValue" is changed
     globally, then algorithm goes into and out of define mode for each variable,
     rather than collecting all information in first pass and replacing all data in second pass.
     This is because ncatted was originally designed to change only metadata and so was
     architected differently from other NCO operators. */
  if(
     aed.att_nm /* Linux strcmp() dumps core if attribute name is blank */
     && strcmp(aed.att_nm,nco_mss_val_sng_get()) == 0 /* Current attribute is "_FillValue" */
     && var_id != NC_GLOBAL /* Current attribute is not global */
     && (aed.mode == aed_modify || aed.mode == aed_overwrite)  /* Modifying or overwriting existing value */
     && rcd == NC_NOERR /* Only when existing _FillValue attribute is modified */
     && att_sz == 1L /* Old _FillValue attribute must be of size 1 */
     && aed.sz == 1L /* New _FillValue attribute must be of size 1 */
     ){

    int *dmn_id;
    long *dmn_sz;
    long *dmn_srt;
    long idx;
    long var_sz=long_CEWI;
    ptr_unn mss_val_crr;
    ptr_unn mss_val_new;
    ptr_unn var_val;
    var_sct *var=NULL_CEWI;

    if(dbg_lvl_get() >= nco_dbg_std) (void)fprintf(stdout,"%s: INFO Replacing missing value data in variable %s\n",prg_nm_get(),var_nm);

    /* Take file out of define mode */
    (void)nco_enddef(nc_id);
  
    /* Initialize (partially) variable structure */
    var=(var_sct *)nco_malloc(sizeof(var_sct));
    var->nc_id=nc_id;
    var->id=var_id;
    var->sz=1L;

    /* Get type of variable and number of dimensions */
    (void)nco_inq_var(nc_id,var_id,(char *)NULL,&var->type,&var->nbr_dim,(int *)NULL,(int *)NULL);
    dmn_id=(int *)nco_malloc(var->nbr_dim*sizeof(int));
    dmn_sz=(long *)nco_malloc(var->nbr_dim*sizeof(long));
    dmn_srt=(long *)nco_malloc(var->nbr_dim*sizeof(long));
    (void)nco_inq_vardimid(nc_id,var_id,dmn_id);

    /* Get dimension sizes and construct variable size */
    for(idx=0;idx<var->nbr_dim;idx++){
      (void)nco_inq_dimlen(nc_id,dmn_id[idx],dmn_sz+idx);
      var->sz*=dmn_sz[idx];
      dmn_srt[idx]=0L;
    } /* end loop over dim */
    var->dmn_id=dmn_id;
    var->cnt=dmn_sz;
    var->srt=dmn_srt;
      
    /* Place nco_var_get() code inline since var struct is not truly complete */
    if((var->val.vp=(void *)nco_malloc_flg(var->sz*nco_typ_lng(var->type))) == NULL){
      (void)fprintf(stdout,"%s: ERROR Unable to malloc() %ld*%lu bytes in nco_aed_prc()\n",prg_nm_get(),var->sz,(unsigned long)nco_typ_lng(var->type));
      nco_exit(EXIT_FAILURE); 
    } /* end if */
    if(var->sz > 1L){
      (void)nco_get_vara(nc_id,var_id,var->srt,var->cnt,var->val.vp,var->type);
    }else{
      (void)nco_get_var1(nc_id,var_id,var->srt,var->val.vp,var->type);
    } /* end else */
    
    /* Get current missing value attribute */
    var->mss_val.vp=NULL;
    var->has_mss_val=nco_mss_val_get(nc_id,var);

    /* Sanity check */
    if(var->has_mss_val == False){
      (void)fprintf(stdout,"%s: ERROR variable %s does not have \"%s\" attribute in nco_aed_prc()\n",prg_nm_get(),var_nm,nco_mss_val_sng_get());
      nco_exit(EXIT_FAILURE);
    } /* end if */

    /* Shortcuts to avoid indirection */
    var_val=var->val;
    var_sz=var->sz;

    /* Get new and old missing values in same type as variable */
    mss_val_crr.vp=(void *)nco_malloc(att_sz*nco_typ_lng(var->type));
    mss_val_new.vp=(void *)nco_malloc(aed.sz*nco_typ_lng(var->type));

    (void)nco_val_cnf_typ(var->type,var->mss_val,var->type,mss_val_crr); 
    (void)nco_val_cnf_typ(aed.type,aed.val,var->type,mss_val_new);

    /* Typecast pointer to values before access */
    (void)cast_void_nctype(var->type,&var_val);
    (void)cast_void_nctype(var->type,&mss_val_crr);
    (void)cast_void_nctype(var->type,&mss_val_new);
  
    switch(var->type){
    case NC_FLOAT: for(idx=0L;idx<var_sz;idx++) {if(var_val.fp[idx] == *mss_val_crr.fp) var_val.fp[idx]=*mss_val_new.fp;} break;
    case NC_DOUBLE: for(idx=0L;idx<var_sz;idx++) {if(var_val.dp[idx] == *mss_val_crr.dp) var_val.dp[idx]=*mss_val_new.dp;} break;
    case NC_INT: for(idx=0L;idx<var_sz;idx++) {if(var_val.ip[idx] == *mss_val_crr.ip) var_val.ip[idx]=*mss_val_new.ip;} break;
    case NC_SHORT: for(idx=0L;idx<var_sz;idx++) {if(var_val.sp[idx] == *mss_val_crr.sp) var_val.sp[idx]=*mss_val_new.sp;} break;
    case NC_CHAR: for(idx=0L;idx<var_sz;idx++) {if(var_val.cp[idx] == *mss_val_crr.cp) var_val.cp[idx]=*mss_val_new.cp;} break;
    case NC_BYTE: for(idx=0L;idx<var_sz;idx++) {if(var_val.bp[idx] == *mss_val_crr.bp) var_val.bp[idx]=*mss_val_new.bp;} break;
    case NC_UBYTE: for(idx=0L;idx<var_sz;idx++) {if(var_val.ubp[idx] == *mss_val_crr.ubp) var_val.ubp[idx]=*mss_val_new.ubp;} break;
    case NC_USHORT: for(idx=0L;idx<var_sz;idx++) {if(var_val.usp[idx] == *mss_val_crr.usp) var_val.usp[idx]=*mss_val_new.usp;} break;
    case NC_UINT: for(idx=0L;idx<var_sz;idx++) {if(var_val.uip[idx] == *mss_val_crr.uip) var_val.uip[idx]=*mss_val_new.uip;} break;
    case NC_INT64: for(idx=0L;idx<var_sz;idx++) {if(var_val.i64p[idx] == *mss_val_crr.i64p) var_val.i64p[idx]=*mss_val_new.i64p;} break;
    case NC_UINT64: for(idx=0L;idx<var_sz;idx++) {if(var_val.ui64p[idx] == *mss_val_crr.ui64p) var_val.ui64p[idx]=*mss_val_new.ui64p;} break;
    case NC_STRING: for(idx=0L;idx<var_sz;idx++) {if(var_val.sngp[idx] == *mss_val_crr.sngp) var_val.sngp[idx]=*mss_val_new.sngp;} break;
    default: nco_dfl_case_nc_type_err(); break;
    } /* end switch */

    /* Un-typecast the pointer to values after access */
    (void)cast_nctype_void(var->type,&var_val);
    (void)cast_nctype_void(var->type,&mss_val_crr);
    (void)cast_nctype_void(var->type,&mss_val_new);

    /* Write to disk */
    if(var->nbr_dim == 0){
      (void)nco_put_var1(nc_id,var_id,var->srt,var->val.vp,var->type);
    }else{ /* end if variable is scalar */
      (void)nco_put_vara(nc_id,var_id,var->srt,var->cnt,var->val.vp,var->type);
    } /* end else */

    /* Free memory */
    mss_val_crr.vp=nco_free(mss_val_crr.vp);
    mss_val_new.vp=nco_free(mss_val_new.vp);
    var->mss_val.vp=nco_free(var->mss_val.vp);
    var->val.vp=nco_free(var->val.vp);
    var->dmn_id=(int *)nco_free(var->dmn_id);
    var->srt=(long *)nco_free(var->srt);
    var->cnt=(long *)nco_free(var->cnt);
    /* 20050704 try and use nco_free() to avoid valgrind error message */
    var=(var_sct *)nco_free(var);

    /* Put file back in define mode */
    (void)nco_redef(nc_id);
  } /* end if replacing missing value data */

  /* Change metadata (as written, this must be done after _FillValue data is replaced) */

#ifdef NCO_NETCDF4_AND_FILLVALUE
  /* Bold hack which gets around problem of modifying netCDF4 "_FillValue" attributes
     netCDF4 does not allow this by default, though netCDF3 does
     Change attribute name to att_nm_tmp, modify value, then restore name */

  /* Check if file is netCDF3 classic with netCDF4 library
     If so, do not kludge. NB: create global variable for output file format? */
  { /* scope for fl_fmt temporary */
    int fl_fmt; 
    (void)nco_inq_format(nc_id,&fl_fmt);
    flg_netCDF4=(fl_fmt==NC_FORMAT_NETCDF4 || fl_fmt==NC_FORMAT_NETCDF4_CLASSIC);
  } /* end scope */

  if(flg_netCDF4 && !strcmp(aed.att_nm,nco_mss_val_sng_get()) && aed.mode != aed_delete){
    if(aed.mode != aed_create) (void)nco_rename_att(nc_id,var_id,aed.att_nm,att_nm_tmp);
    strcpy(aed.att_nm,att_nm_tmp); 
  } /* endif libnetCDF may have netCDF4 restrictions */
#endif /* !NCO_NETCDF4_AND_FILLVALUE */

  switch(aed.mode){
  case aed_append:	
    if(rcd == NC_NOERR){
      /* Append to existing attribute value */
      if(aed.type != att_typ){
	(void)fprintf(stdout,"%s: ERROR %s attribute %s is of type %s not %s, unable to append\n",prg_nm_get(),var_nm,aed.att_nm,nco_typ_sng(att_typ),nco_typ_sng(aed.type));
	nco_exit(EXIT_FAILURE);
      } /* end if */
      att_val_new=(void *)nco_malloc((att_sz+aed.sz)*nco_typ_lng(aed.type));
      (void)nco_get_att(nc_id,var_id,aed.att_nm,(void *)att_val_new,aed.type);
      /* NB: Following assumes sizeof(char) = 1 byte */
      (void)memcpy((void *)((char *)att_val_new+att_sz*nco_typ_lng(aed.type)),
		   (void *)aed.val.vp,
		   aed.sz*nco_typ_lng(aed.type));
      (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,att_sz+aed.sz,att_val_new);
      att_val_new=nco_free(att_val_new);
    }else{
      /* Create new attribute */
      (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp);
    } /* end else */
    break;
  case aed_create:	
    if(rcd != NC_NOERR) (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp);  
    break;
  case aed_delete:	
    /* Delete specified attribute if attribute name was specified... */
    if(aed.att_nm){
      /* ...and if attribute is known to exist from previous inquire call... */
      if(rcd == NC_NOERR) (void)nco_del_att(nc_id,var_id,aed.att_nm);
    }else{
      /* ...else delete all attributes for this variable... */
      while(nbr_att){
	(void)nco_inq_attname(nc_id,var_id,nbr_att-1,att_nm);
	(void)nco_del_att(nc_id,var_id,att_nm);
	nbr_att--;
      } /* end while */
    } /* end else */
    break;
  case aed_modify:	
    if(rcd == NC_NOERR) (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp);
    break;
  case aed_overwrite:	
    (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp);  
    break;
  default: 
    break;
  } /* end switch */

#ifdef NCO_NETCDF4_AND_FILLVALUE
    if(flg_netCDF4 && !strcmp(aed.att_nm,att_nm_tmp) && aed.mode != aed_delete){
      (void)nco_rename_att(nc_id,var_id,att_nm_tmp,nco_mss_val_sng_get());
      /* Restore original name (space already allocated) */
      strcpy(aed.att_nm,nco_mss_val_sng_get()); 
    } /* !flg_netCDF4 */
#endif /* !NCO_NETCDF4_AND_FILLVALUE */

} /* end nco_aed_prc() */